From 76f0678820bf5b00b6a3d27b2d2daacc1ab5d7fc Mon Sep 17 00:00:00 2001 From: sehraf Date: Mon, 26 Oct 2020 21:45:03 +0100 Subject: [PATCH 01/20] add fork of libsam3 add funtion to get i2p certificate crypto algo names --- .gitignore | 2 + libretroshare/src/util/i2pcommon.cpp | 113 +- libretroshare/src/util/i2pcommon.h | 9 + supportlibs/libsam3/Makefile | 41 + supportlibs/libsam3/README.md | 18 + supportlibs/libsam3/examples/libsam3 | 1 + supportlibs/libsam3/examples/sam3/README.md | 26 + supportlibs/libsam3/examples/sam3/dgramc.c | 116 ++ supportlibs/libsam3/examples/sam3/dgrams.c | 113 ++ .../libsam3/examples/sam3/namelookup.c | 43 + supportlibs/libsam3/examples/sam3/samtest.c | 51 + supportlibs/libsam3/examples/sam3/streamc.c | 87 + supportlibs/libsam3/examples/sam3/streams.c | 72 + supportlibs/libsam3/examples/sam3a/test00.c | 178 ++ supportlibs/libsam3/examples/sam3a/test_sc.c | 205 ++ supportlibs/libsam3/examples/sam3a/test_ss.c | 236 +++ supportlibs/libsam3/src/ext/tinytest.c | 466 +++++ supportlibs/libsam3/src/ext/tinytest.h | 104 + supportlibs/libsam3/src/ext/tinytest_macros.h | 219 ++ supportlibs/libsam3/src/libsam3/libsam3.c | 1304 ++++++++++++ supportlibs/libsam3/src/libsam3/libsam3.h | 280 +++ supportlibs/libsam3/src/libsam3a/libsam3a.c | 1757 +++++++++++++++++ supportlibs/libsam3/src/libsam3a/libsam3a.h | 361 ++++ supportlibs/libsam3/test/libsam3/test_b32.c | 51 + supportlibs/libsam3/test/test.c | 12 + 25 files changed, 5858 insertions(+), 7 deletions(-) create mode 100644 supportlibs/libsam3/Makefile create mode 100644 supportlibs/libsam3/README.md create mode 120000 supportlibs/libsam3/examples/libsam3 create mode 100644 supportlibs/libsam3/examples/sam3/README.md create mode 100644 supportlibs/libsam3/examples/sam3/dgramc.c create mode 100644 supportlibs/libsam3/examples/sam3/dgrams.c create mode 100644 supportlibs/libsam3/examples/sam3/namelookup.c create mode 100644 supportlibs/libsam3/examples/sam3/samtest.c create mode 100644 supportlibs/libsam3/examples/sam3/streamc.c create mode 100644 supportlibs/libsam3/examples/sam3/streams.c create mode 100644 supportlibs/libsam3/examples/sam3a/test00.c create mode 100644 supportlibs/libsam3/examples/sam3a/test_sc.c create mode 100644 supportlibs/libsam3/examples/sam3a/test_ss.c create mode 100644 supportlibs/libsam3/src/ext/tinytest.c create mode 100644 supportlibs/libsam3/src/ext/tinytest.h create mode 100644 supportlibs/libsam3/src/ext/tinytest_macros.h create mode 100644 supportlibs/libsam3/src/libsam3/libsam3.c create mode 100644 supportlibs/libsam3/src/libsam3/libsam3.h create mode 100644 supportlibs/libsam3/src/libsam3a/libsam3a.c create mode 100644 supportlibs/libsam3/src/libsam3a/libsam3a.h create mode 100644 supportlibs/libsam3/test/libsam3/test_b32.c create mode 100644 supportlibs/libsam3/test/test.c diff --git a/.gitignore b/.gitignore index 2b880085b..6f9356d05 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ Thumbs.db *.pro.user .kdev4 *.kdev4 + +!supportlibs/libsam3/Makefile diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index ec2ebfd6b..bca7ae110 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -70,13 +70,14 @@ std::string publicKeyFromPrivate(std::string const &priv) uint8_t certType = 0; uint16_t len = 0; uint16_t signingKeyType = 0; - uint16_t cryptKey = 0; + uint16_t cryptKeyType = 0; // only used for easy break do { try { // jump to certificate p += publicKeyLen; + // try to read type and length certType = *p++; len = readTwoBytesBE(p); @@ -87,7 +88,7 @@ std::string publicKeyFromPrivate(std::string const &priv) /* * CertType.Null * type null is followed by 0x00 0x00 - * so has to be 0! + * so len has to be 0! */ RS_DBG("cert is CertType.Null"); publicKeyLen += 3; // add 0x00 0x00 0x00 @@ -119,7 +120,7 @@ std::string publicKeyFromPrivate(std::string const &priv) // likely 7 signingKeyType = readTwoBytesBE(p); - RS_DBG("signing pubkey type ", certType); + RS_DBG("signing pubkey type ", signingKeyType); if (signingKeyType >= 3 && signingKeyType <= 6) { RS_DBG("signing pubkey type ", certType, " has oversize"); // calculate oversize @@ -137,18 +138,18 @@ std::string publicKeyFromPrivate(std::string const &priv) return std::string(); } - publicKeyLen += values.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted + publicKeyLen += values.first - 128; // 128 = default DSA key length = the space that can be used before the key must be splitted } // Crypto Public Key // likely 0 - cryptKey = readTwoBytesBE(p); - RS_DBG("crypto pubkey type ", cryptKey); + cryptKeyType = readTwoBytesBE(p); + RS_DBG("crypto pubkey type ", cryptKeyType); // info: these are all smaller than the default 256 bytes, so no oversize calculation is needed break; } catch (const std::out_of_range &e) { - RS_DBG("hit exception! ", e.what()); + RS_DBG("hit an exception! ", e.what()); return std::string(); } } while(false); @@ -160,4 +161,102 @@ std::string publicKeyFromPrivate(std::string const &priv) return pub; } +bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &cryptoKey) +{ + // creat a copy to work on, need to convert it to standard base64 + auto key_copy(key); + std::replace(key_copy.begin(), key_copy.end(), '~', '/'); + // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too + // std::replace(copy.begin(), copy.end(), '-', '+'); + + // get raw data + std::vector data; + RsBase64::decode(key_copy, data); + + auto p = data.cbegin(); + + constexpr size_t publicKeyLen = 256 + 128; // default length (bytes) + uint8_t certType = 0; + uint16_t signingKeyType = 0; + uint16_t cryptKeyType = 0; + + // try to read types + try { + // jump to certificate + p += publicKeyLen; + + // try to read type and skip length + certType = *p++; + p += 2; + + // only 0 and 5 are used / valid at this point + // check for == 0 + if (certType == static_cast::type>(CertType::Null)) { + RS_DBG("cert is CertType.Null"); + + signingKey = "DSA_SHA1"; + cryptoKey = "ElGamal"; + return true; + } + + // check for != 5 + if (certType != static_cast::type>(CertType::Key)) { + // unsupported + RS_DBG("cert type ", certType, " is unsupported"); + return false; + } + + RS_DBG("cert is CertType.Key"); + + // Signing Public Key + // likely 7 + signingKeyType = readTwoBytesBE(p); + RS_DBG("signing pubkey type ", signingKeyType); + + // Crypto Public Key + // likely 0 + cryptKeyType = readTwoBytesBE(p); + RS_DBG("crypto pubkey type ", cryptKeyType); + } catch (const std::out_of_range &e) { + RS_DBG("hit an exception! ", e.what()); + return false; + } + + // now convert to string (this would be easier with c++17) +#define HELPER(a, b, c) \ + case static_cast::type>(a::c): \ + b = "c"; \ + break; + + switch (signingKeyType) { + HELPER(SigningKeyType, signingKey, DSA_SHA1) + HELPER(SigningKeyType, signingKey, ECDSA_SHA256_P256) + HELPER(SigningKeyType, signingKey, ECDSA_SHA384_P384) + HELPER(SigningKeyType, signingKey, ECDSA_SHA512_P521) + HELPER(SigningKeyType, signingKey, RSA_SHA256_2048) + HELPER(SigningKeyType, signingKey, RSA_SHA384_3072) + HELPER(SigningKeyType, signingKey, RSA_SHA512_4096) + HELPER(SigningKeyType, signingKey, EdDSA_SHA512_Ed25519) + HELPER(SigningKeyType, signingKey, EdDSA_SHA512_Ed25519ph) + HELPER(SigningKeyType, signingKey, RedDSA_SHA512_Ed25519) + default: + RsWarn("unkown signing key type:", signingKeyType); + return false; + } + + switch (cryptKeyType) { + HELPER(CryptoKeyType, cryptoKey, ElGamal) + HELPER(CryptoKeyType, cryptoKey, P256) + HELPER(CryptoKeyType, cryptoKey, P384) + HELPER(CryptoKeyType, cryptoKey, P521) + HELPER(CryptoKeyType, cryptoKey, X25519) + default: + RsWarn("unkown crypto key type:", cryptKeyType); + return false; + } +#undef HELPER + + return true; +} + } // namespace i2p diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h index 1fd152079..e41b61010 100644 --- a/libretroshare/src/util/i2pcommon.h +++ b/libretroshare/src/util/i2pcommon.h @@ -208,6 +208,15 @@ std::string keyToBase32Addr(const std::string &key); */ std::string publicKeyFromPrivate(const std::string &priv); +/** + * @brief getKeyTypes returns the name of the utilized algorithms used by the key + * @param key public key (private works, too) + * @param signingKey name of the signing key, e.g. DSA_SHA1 + * @param cryptoKey name of the crpyto key, e.g. ElGamal + * @return true on success, false otherwise + */ +bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &cryptoKey); + } // namespace i2p #endif // I2PCOMMON_H diff --git a/supportlibs/libsam3/Makefile b/supportlibs/libsam3/Makefile new file mode 100644 index 000000000..fc6f52245 --- /dev/null +++ b/supportlibs/libsam3/Makefile @@ -0,0 +1,41 @@ +CFLAGS := -Wall -g -O2 -std=gnu99 + +SRCS := \ + src/libsam3/libsam3.c \ + src/libsam3a/libsam3a.c + +TESTS := \ + src/ext/tinytest.c \ + test/test.c \ + test/libsam3/test_b32.c + +LIB_OBJS := ${SRCS:.c=.o} +TEST_OBJS := ${TESTS:.c=.o} + +OBJS := ${LIB_OBJS} ${TEST_OBJS} + +LIB := libsam3.a + + +all: build check + +check: libsam3-tests + ./libsam3-tests + +build: ${LIB} + +${LIB}: ${LIB_OBJS} + ${AR} -sr ${LIB} ${LIB_OBJS} + +libsam3-tests: ${TEST_OBJS} ${LIB} + ${CC} $^ -o $@ + +clean: + rm -f libsam3-tests ${LIB} ${OBJS} examples/sam3/samtest + +%.o: %.c Makefile + ${CC} ${CFLAGS} -c $< -o $@ + +fmt: + find . -name '*.c' -exec clang-format -i {} \; + find . -name '*.h' -exec clang-format -i {} \; diff --git a/supportlibs/libsam3/README.md b/supportlibs/libsam3/README.md new file mode 100644 index 000000000..98dfe4205 --- /dev/null +++ b/supportlibs/libsam3/README.md @@ -0,0 +1,18 @@ +# libsam3 + +[![Build Status](https://travis-ci.org/i2p/libsam3.svg?branch=master)](https://travis-ci.org/i2p/libsam3) + +A C library for the [SAM v3 API](https://geti2p.net/en/docs/api/samv3). + +## Development Status + +Unmaintained, but PRs welcome! + +## Usage + +Copy the two files from one of the following locations into your codebase: + +- `src/libsam3` - Synchronous implementation. +- `src/libsam3a` - Asynchronous implementation. + +See `examples/` for how to use various parts of the API. diff --git a/supportlibs/libsam3/examples/libsam3 b/supportlibs/libsam3/examples/libsam3 new file mode 120000 index 000000000..136138896 --- /dev/null +++ b/supportlibs/libsam3/examples/libsam3 @@ -0,0 +1 @@ +../src/libsam3 \ No newline at end of file diff --git a/supportlibs/libsam3/examples/sam3/README.md b/supportlibs/libsam3/examples/sam3/README.md new file mode 100644 index 000000000..1228e4984 --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/README.md @@ -0,0 +1,26 @@ +Examples +======== + +These examples show various ways of using libsam3 to enable i2p in your +application, and are also useful in other ways. If you implement an i2p +application library in another language, making variants basic tools wouldn't be +the worst way to make sure that it works. + +building +-------- + +Once you have build the library in the root of this repository by running make +all, you can build all these examples at once by running + + make + +in this directory. I think it makes things easier to experiment with quickly. + +namelookup +---------- + +Namelookup uses the SAM API to find the base64 destination of an readable "jump" +or base32 i2p address. You can use it like this: + + ./lookup i2p-projekt.i2p + diff --git a/supportlibs/libsam3/examples/sam3/dgramc.c b/supportlibs/libsam3/examples/sam3/dgramc.c new file mode 100644 index 000000000..898a9aec1 --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/dgramc.c @@ -0,0 +1,116 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +// comment the following if you don't want to stress UDP with 'big' datagram +// seems that up to 32000 bytes can be used for localhost +// note that we need 516+6+? bytes for header; lets reserve 1024 bytes for it +#define BIG (32000 - 1024) + +#define KEYFILE "dgrams.key" + +int main(int argc, char *argv[]) { + Sam3Session ses; + char buf[1024]; + char destkey[517] = {0}; // 516 chars + \0 + int sz; + // + libsam3_debug = 1; + // + if (argc < 2) { + FILE *fl = fopen(KEYFILE, "rb"); + // + if (fl != NULL) { + if (fread(destkey, 516, 1, fl) == 1) { + fclose(fl); + goto ok; + } + fclose(fl); + } + printf("usage: dgramc PUBKEY\n"); + return 1; + } else { + if (strlen(argv[1]) != 516) { + fprintf(stderr, "FATAL: invalid key length!\n"); + return 1; + } + strcpy(destkey, argv[1]); + } + // +ok: + printf("creating session...\n"); + /* create TRANSIENT session with temporary disposible destination */ + if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, + SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_DGRAM, 4, + NULL) < 0) { + fprintf(stderr, "FATAL: can't create session\n"); + return 1; + } + /* send datagram */ + printf("sending test datagram...\n"); + if (sam3DatagramSend(&ses, destkey, "test", 4) < 0) { + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + /** receive reply */ + if ((sz = sam3DatagramReceive(&ses, buf, sizeof(buf) - 1)) < 0) { + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + /** null terminated string */ + buf[sz] = 0; + printf("received: [%s]\n", buf); + // +#ifdef BIG + { + char *big = calloc(BIG + 1024, sizeof(char)); + /** generate random string */ + sam3GenChannelName(big, BIG + 1023, BIG + 1023); + printf("sending BIG datagram...\n"); + if (sam3DatagramSend(&ses, destkey, big, BIG) < 0) { + free(big); + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + if ((sz = sam3DatagramReceive(&ses, big, BIG + 512)) < 0) { + free(big); + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + big[sz] = 0; + printf("received (%d): [%s]\n", sz, big); + free(big); + } +#endif + // + printf("sending quit datagram...\n"); + if (sam3DatagramSend(&ses, destkey, "quit", 4) < 0) { + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + if ((sz = sam3DatagramReceive(&ses, buf, sizeof(buf) - 1)) < 0) { + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + buf[sz] = 0; + printf("received: [%s]\n", buf); + // + sam3CloseSession(&ses); + return 0; +error: + sam3CloseSession(&ses); + return 1; +} diff --git a/supportlibs/libsam3/examples/sam3/dgrams.c b/supportlibs/libsam3/examples/sam3/dgrams.c new file mode 100644 index 000000000..1fe0fc0f7 --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/dgrams.c @@ -0,0 +1,113 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +#define KEYFILE "dgrams.key" + +int main(int argc, char *argv[]) { + Sam3Session ses; + char privkey[1024], pubkey[1024], buf[33 * 1024]; + + /** quit command */ + const char *quitstr = "quit"; + const size_t quitlen = strlen(quitstr); + + /** reply response */ + const char *replystr = "reply: "; + const size_t replylen = strlen(replystr); + + FILE *fl; + // + libsam3_debug = 1; + // + + /** generate new destination keypair */ + printf("generating keys...\n"); + if (sam3GenerateKeys(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, 4) < 0) { + fprintf(stderr, "FATAL: can't generate keys\n"); + return 1; + } + /** copy keypair into local buffer */ + strncpy(pubkey, ses.pubkey, sizeof(pubkey)); + strncpy(privkey, ses.privkey, sizeof(privkey)); + /** create sam session */ + printf("creating session...\n"); + if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, privkey, + SAM3_SESSION_DGRAM, 5, NULL) < 0) { + fprintf(stderr, "FATAL: can't create session\n"); + return 1; + } + /** make sure we have the right destination */ + // FIXME: probably not needed + if (strcmp(pubkey, ses.pubkey) != 0) { + fprintf(stderr, "FATAL: destination keys don't match\n"); + sam3CloseSession(&ses); + return 1; + } + /** print destination to stdout */ + printf("PUB KEY\n=======\n%s\n=======\n", ses.pubkey); + if ((fl = fopen(KEYFILE, "wb")) != NULL) { + /** write public key to keyfile */ + fwrite(pubkey, strlen(pubkey), 1, fl); + fclose(fl); + } + + /* now listen for UDP packets */ + printf("starting main loop...\n"); + for (;;) { + /** save replylen bytes for out reply at begining */ + char *datagramBuf = buf + replylen; + const size_t datagramMaxLen = sizeof(buf) - replylen; + int sz, isquit; + printf("waiting for datagram...\n"); + /** blocks until we get a UDP packet */ + if ((sz = sam3DatagramReceive(&ses, datagramBuf, datagramMaxLen) < 0)) { + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + /** ensure null terminated string */ + datagramBuf[sz] = 0; + /** print out datagram payload to user */ + printf("FROM\n====\n%s\n====\n", ses.destkey); + printf("SIZE=%d\n", sz); + printf("data: [%s]\n", datagramBuf); + /** check for "quit" */ + isquit = (sz == quitlen && memcmp(datagramBuf, quitstr, quitlen) == 0); + /** echo datagram back to sender with "reply: " at the beginning */ + memcpy(buf, replystr, replylen); + + if (sam3DatagramSend(&ses, ses.destkey, buf, sz + replylen) < 0) { + fprintf(stderr, "ERROR: %s\n", ses.error); + goto error; + } + /** if we got a quit command wait for 10 seconds and break out of the + * mainloop */ + if (isquit) { + printf("shutting down...\n"); + sleep(10); /* let dgram reach it's destination */ + break; + } + } + /** close session and delete keyfile */ + sam3CloseSession(&ses); + unlink(KEYFILE); + return 0; +error: + /** error case, close session, delete keyfile and return exit code 1 */ + sam3CloseSession(&ses); + unlink(KEYFILE); + return 1; +} diff --git a/supportlibs/libsam3/examples/sam3/namelookup.c b/supportlibs/libsam3/examples/sam3/namelookup.c new file mode 100644 index 000000000..772e4aceb --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/namelookup.c @@ -0,0 +1,43 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +int main(int argc, char *argv[]) { + Sam3Session ses; + // + // + libsam3_debug = 1; + // + if (argc < 2) { + printf("usage: %s name [name...]\n", argv[0]); + return 1; + } + /** for each name in arguments ... */ + for (int n = 1; n < argc; ++n) { + if (!getenv("I2P_LOOKUP_QUIET")) { + fprintf(stdout, "%s ... ", argv[n]); + fflush(stdout); + } + /** do oneshot name lookup */ + if (sam3NameLookup(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, argv[n]) >= + 0) { + fprintf(stdout, "%s\n\n", ses.destkey); + } else { + fprintf(stdout, "FAILED [%s]\n", ses.error); + } + } + // + return 0; +} diff --git a/supportlibs/libsam3/examples/sam3/samtest.c b/supportlibs/libsam3/examples/sam3/samtest.c new file mode 100644 index 000000000..b2d0b9983 --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/samtest.c @@ -0,0 +1,51 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +int main(int argc, char *argv[]) { + int fd; + SAMFieldList *rep = NULL; + const char *v; + // + libsam3_debug = 1; + // + // + if ((fd = sam3Handshake(NULL, 0, NULL)) < 0) + return 1; + // + if (sam3tcpPrintf(fd, "DEST GENERATE\n") < 0) + goto error; + rep = sam3ReadReply(fd); + // sam3DumpFieldList(rep); + if (!sam3IsGoodReply(rep, "DEST", "REPLY", "PUB", NULL)) + goto error; + if (!sam3IsGoodReply(rep, "DEST", "REPLY", "PRIV", NULL)) + goto error; + v = sam3FindField(rep, "PUB"); + printf("PUB KEY\n=======\n%s\n", v); + v = sam3FindField(rep, "PRIV"); + printf("PRIV KEY\n========\n%s\n", v); + sam3FreeFieldList(rep); + rep = NULL; + // + sam3FreeFieldList(rep); + sam3tcpDisconnect(fd); + return 0; +error: + sam3FreeFieldList(rep); + sam3tcpDisconnect(fd); + return 1; +} diff --git a/supportlibs/libsam3/examples/sam3/streamc.c b/supportlibs/libsam3/examples/sam3/streamc.c new file mode 100644 index 000000000..672831c3a --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/streamc.c @@ -0,0 +1,87 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +#define KEYFILE "streams.key" + +int main(int argc, char *argv[]) { + Sam3Session ses; + Sam3Connection *conn; + char cmd[1024], destkey[617]; // 616 chars + \0 + // + libsam3_debug = 1; + // + memset(destkey, 0, sizeof(destkey)); + // + if (argc < 2) { + FILE *fl = fopen(KEYFILE, "rb"); + // + if (fl != NULL) { + if (fread(destkey, 616, 1, fl) == 1) { + fclose(fl); + goto ok; + } + fclose(fl); + } + printf("usage: streamc PUBKEY\n"); + return 1; + } else { + if (!sam3CheckValidKeyLength(argv[1])) { + fprintf(stderr, "FATAL: invalid key length! %s %lu\n", argv[1], + strlen(argv[1])); + return 1; + } + strcpy(destkey, argv[1]); + } + // +ok: + printf("creating session...\n"); + // create TRANSIENT session + if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, + SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, 4, + NULL) < 0) { + fprintf(stderr, "FATAL: can't create session\n"); + return 1; + } + // + printf("connecting...\n"); + if ((conn = sam3StreamConnect(&ses, destkey)) == NULL) { + fprintf(stderr, "FATAL: can't connect: %s\n", ses.error); + sam3CloseSession(&ses); + return 1; + } + // + // now waiting for incoming connection + printf("sending test command...\n"); + if (sam3tcpPrintf(conn->fd, "test\n") < 0) + goto error; + if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) + goto error; + printf("echo: %s\n", cmd); + // + printf("sending quit command...\n"); + if (sam3tcpPrintf(conn->fd, "quit\n") < 0) + goto error; + // + sam3CloseConnection(conn); + sam3CloseSession(&ses); + return 0; +error: + fprintf(stderr, "FATAL: some error occured!\n"); + sam3CloseConnection(conn); + sam3CloseSession(&ses); + return 1; +} diff --git a/supportlibs/libsam3/examples/sam3/streams.c b/supportlibs/libsam3/examples/sam3/streams.c new file mode 100644 index 000000000..2fa000c1c --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/streams.c @@ -0,0 +1,72 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +#define KEYFILE "streams.key" + +int main(int argc, char *argv[]) { + Sam3Session ses; + Sam3Connection *conn; + FILE *fl; + // + libsam3_debug = 1; + // + printf("creating session...\n"); + // create TRANSIENT session + if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, + SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, 4, + NULL) < 0) { + fprintf(stderr, "FATAL: can't create session\n"); + return 1; + } + // + printf("PUB KEY\n=======\n%s\n=======\n", ses.pubkey); + if ((fl = fopen(KEYFILE, "wb")) != NULL) { + fwrite(ses.pubkey, strlen(ses.pubkey), 1, fl); + fclose(fl); + } + // + printf("starting stream acceptor...\n"); + if ((conn = sam3StreamAccept(&ses)) == NULL) { + fprintf(stderr, "FATAL: can't accept: %s\n", ses.error); + sam3CloseSession(&ses); + return 1; + } + printf("FROM\n====\n%s\n====\n", conn->destkey); + // + printf("starting main loop...\n"); + for (;;) { + char cmd[256]; + // + if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) + goto error; + printf("cmd: [%s]\n", cmd); + if (strcmp(cmd, "quit") == 0) + break; + // echo command + if (sam3tcpPrintf(conn->fd, "re: %s\n", cmd) < 0) + goto error; + } + // + sam3CloseSession(&ses); + unlink(KEYFILE); + return 0; +error: + fprintf(stderr, "FATAL: some error occured!\n"); + sam3CloseSession(&ses); + unlink(KEYFILE); + return 1; +} diff --git a/supportlibs/libsam3/examples/sam3a/test00.c b/supportlibs/libsam3/examples/sam3a/test00.c new file mode 100644 index 000000000..5596053e9 --- /dev/null +++ b/supportlibs/libsam3/examples/sam3a/test00.c @@ -0,0 +1,178 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../libsam3a/libsam3a.h" + +//////////////////////////////////////////////////////////////////////////////// +static void scbErrorClose(Sam3ASession *ses) { + fprintf(stderr, + "\n===============================\nSESION_ERROR: " + "[%s]\n===============================\n", + ses->error); + sam3aCloseSession(ses); // it's safe here +} + +static void scbNRCreated(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nNAME RESOLVED: [%s]\n", + ses->params); + fprintf(stderr, "PUB: %s\n===============================\n", ses->destkey); + sam3aCloseSession(ses); // it's safe here +} + +static const Sam3ASessionCallbacks scbNR = { + .cbError = scbErrorClose, + .cbCreated = scbNRCreated, + .cbDisconnected = NULL, + .cbDatagramRead = NULL, + .cbDestroy = NULL, +}; + +//////////////////////////////////////////////////////////////////////////////// +static void scbKGCreated(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nKEYS GENERATED\n"); + fprintf(stderr, "\rPRIV: %s\n", ses->privkey); + fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); + sam3aCloseSession(ses); // it's safe here +} + +static const Sam3ASessionCallbacks scbKG = { + .cbError = scbErrorClose, + .cbCreated = scbKGCreated, + .cbDisconnected = NULL, + .cbDatagramRead = NULL, + .cbDestroy = NULL, +}; + +//////////////////////////////////////////////////////////////////////////////// +static void scbError(Sam3ASession *ses) { + fprintf(stderr, + "\n===============================\nSESION_ERROR: " + "[%s]\n===============================\n", + ses->error); +} + +static void scbCreated(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nSESION_CREATED\n"); + fprintf(stderr, "\rPRIV: %s\n", ses->privkey); + fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); + sam3aCancelSession(ses); // it's safe here +} + +static void scbDisconnected(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nSESION_DISCONNECTED\n====" + "===========================\n"); +} + +static void scbDGramRead(Sam3ASession *ses, const void *buf, int bufsize) { + fprintf(stderr, "\n===============================\nSESION_DATAGRAM_READ\n===" + "============================\n"); +} + +static void scbDestroy(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nSESION_DESTROYED\n=======" + "========================\n"); +} + +/** callbacks for our SAM session */ +static const Sam3ASessionCallbacks scb = { + .cbError = scbError, + .cbCreated = scbCreated, + .cbDisconnected = scbDisconnected, + .cbDatagramRead = scbDGramRead, + .cbDestroy = scbDestroy, +}; + +//////////////////////////////////////////////////////////////////////////////// +#define HOST SAM3A_HOST_DEFAULT +//#define HOST "google.com" + +int main(int argc, char *argv[]) { + Sam3ASession ses, snr, skg; + // + // libsam3a_debug = 1; + // + if (sam3aCreateSession(&ses, &scb, HOST, SAM3A_PORT_DEFAULT, + SAM3A_DESTINATION_TRANSIENT, + SAM3A_SESSION_STREAM) < 0) { + fprintf(stderr, "FATAL: can't create main session!\n"); + return 1; + } + // generate keys + if (sam3aGenerateKeys(&skg, &scbKG, HOST, SAM3A_PORT_DEFAULT) < 0) { + sam3aCloseSession(&ses); + fprintf(stderr, "FATAL: can't create keygen session!\n"); + return 1; + } + // do a name lookup for zzz.i2p + if (sam3aNameLookup(&snr, &scbNR, HOST, SAM3A_PORT_DEFAULT, "zzz.i2p") < 0) { + sam3aCloseSession(&skg); + sam3aCloseSession(&ses); + fprintf(stderr, "FATAL: can't create name resolving session!\n"); + return 1; + } + // while we have sessions ... + while (sam3aIsActiveSession(&ses) || sam3aIsActiveSession(&snr) || + sam3aIsActiveSession(&skg)) { + fd_set rds, wrs; + int res, maxfd = 0; + struct timeval to; + // set up file descriptors for select() + FD_ZERO(&rds); + FD_ZERO(&wrs); + // obtain the maximum fd for select() + if (sam3aIsActiveSession(&ses) && + (maxfd = sam3aAddSessionToFDS(&ses, -1, &rds, &wrs)) < 0) + break; + if (sam3aIsActiveSession(&snr) && + (maxfd = sam3aAddSessionToFDS(&snr, -1, &rds, &wrs)) < 0) + break; + if (sam3aIsActiveSession(&skg) && + (maxfd = sam3aAddSessionToFDS(&skg, -1, &rds, &wrs)) < 0) + break; + // set timeout to 1 second + sam3ams2timeval(&to, 1000); + // call select() + res = select(maxfd + 1, &rds, &wrs, NULL, &to); + if (res < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "FATAL: select() error!\n"); + break; + } + if (res == 0) { + // idle, no activity + fprintf(stdout, "."); + fflush(stdout); + } else { + // we have activity, process io + if (sam3aIsActiveSession(&ses)) + sam3aProcessSessionIO(&ses, &rds, &wrs); + if (sam3aIsActiveSession(&snr)) + sam3aProcessSessionIO(&snr, &rds, &wrs); + if (sam3aIsActiveSession(&skg)) + sam3aProcessSessionIO(&skg, &rds, &wrs); + } + } + // close seessions + sam3aCloseSession(&ses); + sam3aCloseSession(&skg); + sam3aCloseSession(&snr); + // exit + return 0; +} diff --git a/supportlibs/libsam3/examples/sam3a/test_sc.c b/supportlibs/libsam3/examples/sam3a/test_sc.c new file mode 100644 index 000000000..8feeb5d52 --- /dev/null +++ b/supportlibs/libsam3/examples/sam3a/test_sc.c @@ -0,0 +1,205 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../libsam3a/libsam3a.h" + +//////////////////////////////////////////////////////////////////////////////// +#define KEYFILE "streams.key" + +//////////////////////////////////////////////////////////////////////////////// +static void ccbError(Sam3AConnection *ct) { + fprintf(stderr, + "\n===============================\nCONNECTION_ERROR: " + "[%s]\n===============================\n", + ct->error); +} + +static void ccbDisconnected(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_" + "DISCONNECTED\n===============================\n"); +} + +static void ccbConnected(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_CONNECTED\n===" + "============================\n"); + // sam3aCancelConnection(ct); // cbSent() will not be called +} + +static void ccbAccepted(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_ACCEPTED\n====" + "===========================\n"); +} + +static void ccbSent(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_WANTBYTES\n===" + "============================\n"); + // sam3aCancelConnection(ct); + // sam3aCancelSession(ct->ses); // hehe + fprintf(stderr, "(%p)\n", ct->udata); + // + switch ((intptr_t)ct->udata) { + case 0: + if (sam3aSend(ct, "test\n", -1) < 0) { + fprintf(stderr, "SEND ERROR!\n"); + sam3aCancelSession(ct->ses); // hehe + } + break; + case 1: + if (sam3aSend(ct, "quit\n", -1) < 0) { + fprintf(stderr, "SEND ERROR!\n"); + sam3aCancelSession(ct->ses); // hehe + } + break; + default: + return; + } + ct->udata = (void *)(((intptr_t)ct->udata) + 1); +} + +static void ccbRead(Sam3AConnection *ct, const void *buf, int bufsize) { + fprintf(stderr, + "\n===============================\nCONNECTION_GOTBYTES " + "(%d)\n===============================\n", + bufsize); +} + +static void ccbDestroy(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_DESTROY\n=====" + "==========================\n"); +} + +static const Sam3AConnectionCallbacks ccb = { + .cbError = ccbError, + .cbDisconnected = ccbDisconnected, + .cbConnected = ccbConnected, + .cbAccepted = ccbAccepted, + .cbSent = ccbSent, + .cbRead = ccbRead, + .cbDestroy = ccbDestroy, +}; + +//////////////////////////////////////////////////////////////////////////////// +static void scbError(Sam3ASession *ses) { + fprintf(stderr, + "\n===============================\nSESION_ERROR: " + "[%s]\n===============================\n", + ses->error); +} + +static void scbCreated(Sam3ASession *ses) { + char destkey[517]; + FILE *fl; + // + fprintf(stderr, "\n===============================\nSESION_CREATED\n"); + fprintf(stderr, "\rPRIV: %s\n", ses->privkey); + fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); + // + fl = fopen(KEYFILE, "rb"); + // + if (fl == NULL) { + fprintf(stderr, "ERROR: NO KEY FILE!\n"); + sam3aCancelSession(ses); + return; + } + if (fread(destkey, 516, 1, fl) != 1) { + fprintf(stderr, "ERROR: INVALID KEY FILE!\n"); + fclose(fl); + sam3aCancelSession(ses); + return; + } + fclose(fl); + destkey[516] = 0; + if (sam3aStreamConnect(ses, &ccb, destkey) == NULL) { + fprintf(stderr, "ERROR: CAN'T CREATE CONNECTION!\n"); + sam3aCancelSession(ses); + return; + } + fprintf(stderr, "GOON: creating connection...\n"); +} + +static void scbDisconnected(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nSESION_DISCONNECTED\n====" + "===========================\n"); +} + +static void scbDGramRead(Sam3ASession *ses, const void *buf, int bufsize) { + fprintf(stderr, "\n===============================\nSESION_DATAGRAM_READ\n===" + "============================\n"); +} + +static void scbDestroy(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nSESION_DESTROYED\n=======" + "========================\n"); +} + +static const Sam3ASessionCallbacks scb = { + .cbError = scbError, + .cbCreated = scbCreated, + .cbDisconnected = scbDisconnected, + .cbDatagramRead = scbDGramRead, + .cbDestroy = scbDestroy, +}; + +//////////////////////////////////////////////////////////////////////////////// +#define HOST SAM3A_HOST_DEFAULT +//#define HOST "google.com" + +int main(int argc, char *argv[]) { + Sam3ASession ses; + // + libsam3a_debug = 0; + // + if (sam3aCreateSession(&ses, &scb, HOST, SAM3A_PORT_DEFAULT, + SAM3A_DESTINATION_TRANSIENT, + SAM3A_SESSION_STREAM) < 0) { + fprintf(stderr, "FATAL: can't create main session!\n"); + return 1; + } + // + while (sam3aIsActiveSession(&ses)) { + fd_set rds, wrs; + int res, maxfd = 0; + struct timeval to; + // + FD_ZERO(&rds); + FD_ZERO(&wrs); + if (sam3aIsActiveSession(&ses) && + (maxfd = sam3aAddSessionToFDS(&ses, -1, &rds, &wrs)) < 0) + break; + sam3ams2timeval(&to, 1000); + res = select(maxfd + 1, &rds, &wrs, NULL, &to); + if (res < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "FATAL: select() error!\n"); + break; + } + if (res == 0) { + fprintf(stdout, "."); + fflush(stdout); + } else { + if (sam3aIsActiveSession(&ses)) + sam3aProcessSessionIO(&ses, &rds, &wrs); + } + } + // + sam3aCloseSession(&ses); + // + return 0; +} diff --git a/supportlibs/libsam3/examples/sam3a/test_ss.c b/supportlibs/libsam3/examples/sam3a/test_ss.c new file mode 100644 index 000000000..c2ff3399c --- /dev/null +++ b/supportlibs/libsam3/examples/sam3a/test_ss.c @@ -0,0 +1,236 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../libsam3a/libsam3a.h" + +//////////////////////////////////////////////////////////////////////////////// +#define KEYFILE "streams.key" + +//////////////////////////////////////////////////////////////////////////////// +typedef struct { + char *str; + int strsize; + int strused; + int doQuit; +} ConnData; + +static void cdAppendChar(ConnData *d, char ch) { + if (d->strused + 1 >= d->strsize) { + // fuck errors + d->strsize = d->strused + 1024; + d->str = realloc(d->str, d->strsize + 1); + } + d->str[d->strused++] = ch; + d->str[d->strused] = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +static void ccbError(Sam3AConnection *ct) { + fprintf(stderr, + "\n===============================\nCONNECTION_ERROR: " + "[%s]\n===============================\n", + ct->error); +} + +static void ccbDisconnected(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_" + "DISCONNECTED\n===============================\n"); +} + +static void ccbConnected(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_CONNECTED\n===" + "============================\n"); + // sam3aCancelConnection(ct); // cbSent() will not be called +} + +static void ccbAccepted(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_ACCEPTED\n====" + "===========================\n"); + fprintf(stderr, "FROM: %s\n===============================\n", ct->destkey); +} + +static void ccbSent(Sam3AConnection *ct) { + ConnData *d = (ConnData *)ct->udata; + // + fprintf(stderr, "\n===============================\nCONNECTION_WANTBYTES\n===" + "============================\n"); + if (d->doQuit) { + sam3aCancelSession(ct->ses); // hehe + } +} + +static void ccbRead(Sam3AConnection *ct, const void *buf, int bufsize) { + const char *b = (const char *)buf; + ConnData *d = (ConnData *)ct->udata; + // + fprintf(stderr, + "\n===============================\nCONNECTION_GOTBYTES " + "(%d)\n===============================\n", + bufsize); + while (bufsize > 0) { + cdAppendChar(ct->udata, *b); + if (*b == '\n') { + fprintf(stderr, "cmd: %s", d->str); + if (strcasecmp(d->str, "quit\n") == 0) + d->doQuit = 1; + if (sam3aSend(ct, d->str, -1) < 0) { + // sam3aCancelConnection(ct); // hehe + sam3aCancelSession(ct->ses); // hehe + return; + } + d->str[0] = 0; + d->strused = 0; + } + ++b; + --bufsize; + } +} + +static void ccbDestroy(Sam3AConnection *ct) { + fprintf(stderr, "\n===============================\nCONNECTION_DESTROY\n=====" + "==========================\n"); + if (ct->udata != NULL) { + ConnData *d = (ConnData *)ct->udata; + // + if (d->str != NULL) + free(d->str); + free(d); + } +} + +static const Sam3AConnectionCallbacks ccb = { + .cbError = ccbError, + .cbDisconnected = ccbDisconnected, + .cbConnected = ccbConnected, + .cbAccepted = ccbAccepted, + .cbSent = ccbSent, + .cbRead = ccbRead, + .cbDestroy = ccbDestroy, +}; + +//////////////////////////////////////////////////////////////////////////////// +static void scbError(Sam3ASession *ses) { + fprintf(stderr, + "\n===============================\nSESION_ERROR: " + "[%s]\n===============================\n", + ses->error); +} + +static void scbCreated(Sam3ASession *ses) { + FILE *fl; + Sam3AConnection *conn; + // + fprintf(stderr, "\n===============================\nSESION_CREATED\n"); + fprintf(stderr, "\rPRIV: %s\n", ses->privkey); + fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); + // + fl = fopen(KEYFILE, "wb"); + // + if (fl == NULL) { + fprintf(stderr, "ERROR: CAN'T CREATE KEY FILE!\n"); + sam3aCancelSession(ses); + return; + } + if (fwrite(ses->pubkey, 516, 1, fl) != 1) { + fprintf(stderr, "ERROR: CAN'T WRITE KEY FILE!\n"); + fclose(fl); + sam3aCancelSession(ses); + return; + } + fclose(fl); + if ((conn = sam3aStreamAccept(ses, &ccb)) == NULL) { + fprintf(stderr, "ERROR: CAN'T CREATE CONNECTION!\n"); + sam3aCancelSession(ses); + return; + } + // + conn->udata = calloc(1, sizeof(ConnData)); + fprintf(stderr, "GOON: accepting connection...\n"); +} + +static void scbDisconnected(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nSESION_DISCONNECTED\n====" + "===========================\n"); +} + +static void scbDGramRead(Sam3ASession *ses, const void *buf, int bufsize) { + fprintf(stderr, "\n===============================\nSESION_DATAGRAM_READ\n===" + "============================\n"); +} + +static void scbDestroy(Sam3ASession *ses) { + fprintf(stderr, "\n===============================\nSESION_DESTROYED\n=======" + "========================\n"); +} + +static const Sam3ASessionCallbacks scb = { + .cbError = scbError, + .cbCreated = scbCreated, + .cbDisconnected = scbDisconnected, + .cbDatagramRead = scbDGramRead, + .cbDestroy = scbDestroy, +}; + +//////////////////////////////////////////////////////////////////////////////// +#define HOST SAM3A_HOST_DEFAULT +//#define HOST "google.com" + +int main(int argc, char *argv[]) { + Sam3ASession ses; + // + libsam3a_debug = 0; + // + if (sam3aCreateSession(&ses, &scb, HOST, SAM3A_PORT_DEFAULT, + SAM3A_DESTINATION_TRANSIENT, + SAM3A_SESSION_STREAM) < 0) { + fprintf(stderr, "FATAL: can't create main session!\n"); + return 1; + } + // + while (sam3aIsActiveSession(&ses)) { + fd_set rds, wrs; + int res, maxfd = 0; + struct timeval to; + // + FD_ZERO(&rds); + FD_ZERO(&wrs); + if (sam3aIsActiveSession(&ses) && + (maxfd = sam3aAddSessionToFDS(&ses, -1, &rds, &wrs)) < 0) + break; + sam3ams2timeval(&to, 1000); + res = select(maxfd + 1, &rds, &wrs, NULL, &to); + if (res < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "FATAL: select() error!\n"); + break; + } + if (res == 0) { + fprintf(stdout, "."); + fflush(stdout); + } else { + if (sam3aIsActiveSession(&ses)) + sam3aProcessSessionIO(&ses, &rds, &wrs); + } + } + // + sam3aCloseSession(&ses); + // + return 0; +} diff --git a/supportlibs/libsam3/src/ext/tinytest.c b/supportlibs/libsam3/src/ext/tinytest.c new file mode 100644 index 000000000..252f03051 --- /dev/null +++ b/supportlibs/libsam3/src/ext/tinytest.c @@ -0,0 +1,466 @@ +/* tinytest.c -- Copyright 2009-2012 Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifdef TINYTEST_LOCAL +#include "tinytest_local.h" +#endif + +#include +#include +#include +#include + +#ifndef NO_FORKING + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) +/* Workaround for a stupid bug in OSX 10.6 */ +#define FORK_BREAKS_GCOV +#include +#endif +#endif + +#endif /* !NO_FORKING */ + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +#include "tinytest.h" +#include "tinytest_macros.h" + +#define LONGEST_TEST_NAME 16384 + +static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ +static int n_ok = 0; /**< Number of tests that have passed */ +static int n_bad = 0; /**< Number of tests that have failed. */ +static int n_skipped = 0; /**< Number of tests that have been skipped. */ + +static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ +static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ +static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ +const char *verbosity_flag = ""; + +const struct testlist_alias_t *cfg_aliases = NULL; + +enum outcome { SKIP = 2, OK = 1, FAIL = 0 }; +static enum outcome cur_test_outcome = FAIL; +const char *cur_test_prefix = NULL; /**< prefix of the current test group */ +/** Name of the current test, if we haven't logged is yet. Used for --quiet */ +const char *cur_test_name = NULL; + +#ifdef _WIN32 +/* Copy of argv[0] for win32. */ +static char commandname[MAX_PATH + 1]; +#endif + +static void usage(struct testgroup_t *groups, int list_groups) + __attribute__((noreturn)); +static int process_test_option(struct testgroup_t *groups, const char *test); + +static enum outcome testcase_run_bare_(const struct testcase_t *testcase) { + void *env = NULL; + enum outcome outcome; + if (testcase->setup) { + env = testcase->setup->setup_fn(testcase); + if (!env) + return FAIL; + else if (env == (void *)TT_SKIP) + return SKIP; + } + + cur_test_outcome = OK; + testcase->fn(env); + outcome = cur_test_outcome; + + if (testcase->setup) { + if (testcase->setup->cleanup_fn(testcase, env) == 0) + outcome = FAIL; + } + + return outcome; +} + +#define MAGIC_EXITCODE 42 + +#ifndef NO_FORKING + +static enum outcome testcase_run_forked_(const struct testgroup_t *group, + const struct testcase_t *testcase) { +#ifdef _WIN32 + /* Fork? On Win32? How primitive! We'll do what the smart kids do: + we'll invoke our own exe (whose name we recall from the command + line) with a command line that tells it to run just the test we + want, and this time without forking. + + (No, threads aren't an option. The whole point of forking is to + share no state between tests.) + */ + int ok; + char buffer[LONGEST_TEST_NAME + 256]; + STARTUPINFOA si; + PROCESS_INFORMATION info; + DWORD exitcode; + + if (!in_tinytest_main) { + printf("\nERROR. On Windows, testcase_run_forked_ must be" + " called from within tinytest_main.\n"); + abort(); + } + if (opt_verbosity > 0) + printf("[forking] "); + + snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", commandname, + verbosity_flag, group->prefix, testcase->name); + + memset(&si, 0, sizeof(si)); + memset(&info, 0, sizeof(info)); + si.cb = sizeof(si); + + ok = CreateProcessA(commandname, buffer, NULL, NULL, 0, 0, NULL, NULL, &si, + &info); + if (!ok) { + printf("CreateProcess failed!\n"); + return 0; + } + WaitForSingleObject(info.hProcess, INFINITE); + GetExitCodeProcess(info.hProcess, &exitcode); + CloseHandle(info.hProcess); + CloseHandle(info.hThread); + if (exitcode == 0) + return OK; + else if (exitcode == MAGIC_EXITCODE) + return SKIP; + else + return FAIL; +#else + int outcome_pipe[2]; + pid_t pid; + (void)group; + + if (pipe(outcome_pipe)) + perror("opening pipe"); + + if (opt_verbosity > 0) + printf("[forking] "); + pid = fork(); +#ifdef FORK_BREAKS_GCOV + vproc_transaction_begin(0); +#endif + if (!pid) { + /* child. */ + int test_r, write_r; + char b[1]; + close(outcome_pipe[0]); + test_r = testcase_run_bare_(testcase); + assert(0 <= (int)test_r && (int)test_r <= 2); + b[0] = "NYS"[test_r]; + write_r = (int)write(outcome_pipe[1], b, 1); + if (write_r != 1) { + perror("write outcome to pipe"); + exit(1); + } + exit(0); + return FAIL; /* unreachable */ + } else { + /* parent */ + int status, r; + char b[1]; + /* Close this now, so that if the other side closes it, + * our read fails. */ + close(outcome_pipe[1]); + r = (int)read(outcome_pipe[0], b, 1); + if (r == 0) { + printf("[Lost connection!] "); + return FAIL; + } else if (r != 1) { + perror("read outcome from pipe"); + } + waitpid(pid, &status, 0); + close(outcome_pipe[0]); + return b[0] == 'Y' ? OK : (b[0] == 'S' ? SKIP : FAIL); + } +#endif +} + +#endif /* !NO_FORKING */ + +int testcase_run_one(const struct testgroup_t *group, + const struct testcase_t *testcase) { + enum outcome outcome; + + if (testcase->flags & (TT_SKIP | TT_OFF_BY_DEFAULT)) { + if (opt_verbosity > 0) + printf("%s%s: %s\n", group->prefix, testcase->name, + (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED"); + ++n_skipped; + return SKIP; + } + + if (opt_verbosity > 0 && !opt_forked) { + printf("%s%s: ", group->prefix, testcase->name); + } else { + if (opt_verbosity == 0) + printf("."); + cur_test_prefix = group->prefix; + cur_test_name = testcase->name; + } + +#ifndef NO_FORKING + if ((testcase->flags & TT_FORK) && !(opt_forked || opt_nofork)) { + outcome = testcase_run_forked_(group, testcase); + } else { +#else + { +#endif + outcome = testcase_run_bare_(testcase); + } + + if (outcome == OK) { + ++n_ok; + if (opt_verbosity > 0 && !opt_forked) + puts(opt_verbosity == 1 ? "OK" : ""); + } else if (outcome == SKIP) { + ++n_skipped; + if (opt_verbosity > 0 && !opt_forked) + puts("SKIPPED"); + } else { + ++n_bad; + if (!opt_forked) + printf("\n [%s FAILED]\n", testcase->name); + } + + if (opt_forked) { + exit(outcome == OK ? 0 : (outcome == SKIP ? MAGIC_EXITCODE : 1)); + return 1; /* unreachable */ + } else { + return (int)outcome; + } +} + +int tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, + unsigned long flag) { + int i, j; + size_t length = LONGEST_TEST_NAME; + char fullname[LONGEST_TEST_NAME]; + int found = 0; + if (strstr(arg, "..")) + length = strstr(arg, "..") - arg; + for (i = 0; groups[i].prefix; ++i) { + for (j = 0; groups[i].cases[j].name; ++j) { + struct testcase_t *testcase = &groups[i].cases[j]; + snprintf(fullname, sizeof(fullname), "%s%s", groups[i].prefix, + testcase->name); + if (!flag) { /* Hack! */ + printf(" %s", fullname); + if (testcase->flags & TT_OFF_BY_DEFAULT) + puts(" (Off by default)"); + else if (testcase->flags & TT_SKIP) + puts(" (DISABLED)"); + else + puts(""); + } + if (!strncmp(fullname, arg, length)) { + if (set) + testcase->flags |= flag; + else + testcase->flags &= ~flag; + ++found; + } + } + } + return found; +} + +static void usage(struct testgroup_t *groups, int list_groups) { + puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); + puts(" Specify tests by name, or using a prefix ending with '..'"); + puts(" To skip a test, prefix its name with a colon."); + puts(" To enable a disabled test, prefix its name with a plus."); + puts(" Use --list-tests for a list of tests."); + if (list_groups) { + puts("Known tests are:"); + tinytest_set_flag_(groups, "..", 1, 0); + } + exit(0); +} + +static int process_test_alias(struct testgroup_t *groups, const char *test) { + int i, j, n, r; + for (i = 0; cfg_aliases && cfg_aliases[i].name; ++i) { + if (!strcmp(cfg_aliases[i].name, test)) { + n = 0; + for (j = 0; cfg_aliases[i].tests[j]; ++j) { + r = process_test_option(groups, cfg_aliases[i].tests[j]); + if (r < 0) + return -1; + n += r; + } + return n; + } + } + printf("No such test alias as @%s!", test); + return -1; +} + +static int process_test_option(struct testgroup_t *groups, const char *test) { + int flag = TT_ENABLED_; + int n = 0; + if (test[0] == '@') { + return process_test_alias(groups, test + 1); + } else if (test[0] == ':') { + ++test; + flag = TT_SKIP; + } else if (test[0] == '+') { + ++test; + ++n; + if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) { + printf("No such test as %s!\n", test); + return -1; + } + } else { + ++n; + } + if (!tinytest_set_flag_(groups, test, 1, flag)) { + printf("No such test as %s!\n", test); + return -1; + } + return n; +} + +void tinytest_set_aliases(const struct testlist_alias_t *aliases) { + cfg_aliases = aliases; +} + +int tinytest_main(int c, const char **v, struct testgroup_t *groups) { + int i, j, n = 0; + +#ifdef _WIN32 + const char *sp = strrchr(v[0], '.'); + const char *extension = ""; + if (!sp || stricmp(sp, ".exe")) + extension = ".exe"; /* Add an exe so CreateProcess will work */ + snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension); + commandname[MAX_PATH] = '\0'; +#endif + for (i = 1; i < c; ++i) { + if (v[i][0] == '-') { + if (!strcmp(v[i], "--RUNNING-FORKED")) { + opt_forked = 1; + } else if (!strcmp(v[i], "--no-fork")) { + opt_nofork = 1; + } else if (!strcmp(v[i], "--quiet")) { + opt_verbosity = -1; + verbosity_flag = "--quiet"; + } else if (!strcmp(v[i], "--verbose")) { + opt_verbosity = 2; + verbosity_flag = "--verbose"; + } else if (!strcmp(v[i], "--terse")) { + opt_verbosity = 0; + verbosity_flag = "--terse"; + } else if (!strcmp(v[i], "--help")) { + usage(groups, 0); + } else if (!strcmp(v[i], "--list-tests")) { + usage(groups, 1); + } else { + printf("Unknown option %s. Try --help\n", v[i]); + return -1; + } + } else { + int r = process_test_option(groups, v[i]); + if (r < 0) + return -1; + n += r; + } + } + if (!n) + tinytest_set_flag_(groups, "..", 1, TT_ENABLED_); + +#ifdef _IONBF + setvbuf(stdout, NULL, _IONBF, 0); +#endif + + ++in_tinytest_main; + for (i = 0; groups[i].prefix; ++i) + for (j = 0; groups[i].cases[j].name; ++j) + if (groups[i].cases[j].flags & TT_ENABLED_) + testcase_run_one(&groups[i], &groups[i].cases[j]); + + --in_tinytest_main; + + if (opt_verbosity == 0) + puts(""); + + if (n_bad) + printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad, n_bad + n_ok, + n_skipped); + else if (opt_verbosity >= 1) + printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped); + + return (n_bad == 0) ? 0 : 1; +} + +int tinytest_get_verbosity_(void) { return opt_verbosity; } + +void tinytest_set_test_failed_(void) { + if (opt_verbosity <= 0 && cur_test_name) { + if (opt_verbosity == 0) + puts(""); + printf("%s%s: ", cur_test_prefix, cur_test_name); + cur_test_name = NULL; + } + cur_test_outcome = FAIL; +} + +void tinytest_set_test_skipped_(void) { + if (cur_test_outcome == OK) + cur_test_outcome = SKIP; +} + +char *tinytest_format_hex_(const void *val_, unsigned long len) { + const unsigned char *val = (unsigned char *)val_; + char *result, *cp; + size_t i; + + if (!val) + return strdup("null"); + if (!(result = (char *)malloc(len * 2 + 1))) + return strdup(""); + cp = result; + for (i = 0; i < len; ++i) { + *cp++ = "0123456789ABCDEF"[val[i] >> 4]; + *cp++ = "0123456789ABCDEF"[val[i] & 0x0f]; + } + *cp = 0; + return result; +} diff --git a/supportlibs/libsam3/src/ext/tinytest.h b/supportlibs/libsam3/src/ext/tinytest.h new file mode 100644 index 000000000..bdddcbb54 --- /dev/null +++ b/supportlibs/libsam3/src/ext/tinytest.h @@ -0,0 +1,104 @@ +/* tinytest.h -- Copyright 2009-2012 Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYTEST_H_INCLUDED_ +#define TINYTEST_H_INCLUDED_ + +/** Flag for a test that needs to run in a subprocess. */ +#define TT_FORK (1 << 0) +/** Runtime flag for a test we've decided to skip. */ +#define TT_SKIP (1 << 1) +/** Internal runtime flag for a test we've decided to run. */ +#define TT_ENABLED_ (1 << 2) +/** Flag for a test that's off by default. */ +#define TT_OFF_BY_DEFAULT (1 << 3) +/** If you add your own flags, make them start at this point. */ +#define TT_FIRST_USER_FLAG (1 << 4) + +typedef void (*testcase_fn)(void *); + +struct testcase_t; + +/** Functions to initialize/teardown a structure for a testcase. */ +struct testcase_setup_t { + /** Return a new structure for use by a given testcase. */ + void *(*setup_fn)(const struct testcase_t *); + /** Clean/free a structure from setup_fn. Return 1 if ok, 0 on err. */ + int (*cleanup_fn)(const struct testcase_t *, void *); +}; + +/** A single test-case that you can run. */ +struct testcase_t { + const char *name; /**< An identifier for this case. */ + testcase_fn fn; /**< The function to run to implement this case. */ + unsigned long flags; /**< Bitfield of TT_* flags. */ + const struct testcase_setup_t *setup; /**< Optional setup/cleanup fns*/ + void *setup_data; /**< Extra data usable by setup function */ +}; +#define END_OF_TESTCASES \ + { NULL, NULL, 0, NULL, NULL } + +/** A group of tests that are selectable together. */ +struct testgroup_t { + const char *prefix; /**< Prefix to prepend to testnames. */ + struct testcase_t *cases; /** Array, ending with END_OF_TESTCASES */ +}; +#define END_OF_GROUPS \ + { NULL, NULL } + +struct testlist_alias_t { + const char *name; + const char **tests; +}; +#define END_OF_ALIASES \ + { NULL, NULL } + +/** Implementation: called from a test to indicate failure, before logging. */ +void tinytest_set_test_failed_(void); +/** Implementation: called from a test to indicate that we're skipping. */ +void tinytest_set_test_skipped_(void); +/** Implementation: return 0 for quiet, 1 for normal, 2 for loud. */ +int tinytest_get_verbosity_(void); +/** Implementation: Set a flag on tests matching a name; returns number + * of tests that matched. */ +int tinytest_set_flag_(struct testgroup_t *, const char *, int set, + unsigned long); +/** Implementation: Put a chunk of memory into hex. */ +char *tinytest_format_hex_(const void *, unsigned long); + +/** Set all tests in 'groups' matching the name 'named' to be skipped. */ +#define tinytest_skip(groups, named) \ + tinytest_set_flag_(groups, named, 1, TT_SKIP) + +/** Run a single testcase in a single group. */ +int testcase_run_one(const struct testgroup_t *, const struct testcase_t *); + +void tinytest_set_aliases(const struct testlist_alias_t *aliases); + +/** Run a set of testcases from an END_OF_GROUPS-terminated array of groups, + as selected from the command line. */ +int tinytest_main(int argc, const char **argv, struct testgroup_t *groups); + +#endif diff --git a/supportlibs/libsam3/src/ext/tinytest_macros.h b/supportlibs/libsam3/src/ext/tinytest_macros.h new file mode 100644 index 000000000..817734020 --- /dev/null +++ b/supportlibs/libsam3/src/ext/tinytest_macros.h @@ -0,0 +1,219 @@ +/* tinytest_macros.h -- Copyright 2009-2012 Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TINYTEST_MACROS_H_INCLUDED_ +#define TINYTEST_MACROS_H_INCLUDED_ + +/* Helpers for defining statement-like macros */ +#define TT_STMT_BEGIN do { +#define TT_STMT_END \ + } \ + while (0) + +/* Redefine this if your test functions want to abort with something besides + * "goto end;" */ +#ifndef TT_EXIT_TEST_FUNCTION +#define TT_EXIT_TEST_FUNCTION \ + TT_STMT_BEGIN goto end; \ + TT_STMT_END +#endif + +/* Redefine this if you want to note success/failure in some different way. */ +#ifndef TT_DECLARE +#define TT_DECLARE(prefix, args) \ + TT_STMT_BEGIN \ + printf("\n %s %s:%d: ", prefix, __FILE__, __LINE__); \ + printf args; \ + TT_STMT_END +#endif + +/* Announce a failure. Args are parenthesized printf args. */ +#define TT_GRIPE(args) TT_DECLARE("FAIL", args) + +/* Announce a non-failure if we're verbose. */ +#define TT_BLATHER(args) \ + TT_STMT_BEGIN \ + if (tinytest_get_verbosity_() > 1) \ + TT_DECLARE(" OK", args); \ + TT_STMT_END + +#define TT_DIE(args) \ + TT_STMT_BEGIN \ + tinytest_set_test_failed_(); \ + TT_GRIPE(args); \ + TT_EXIT_TEST_FUNCTION; \ + TT_STMT_END + +#define TT_FAIL(args) \ + TT_STMT_BEGIN \ + tinytest_set_test_failed_(); \ + TT_GRIPE(args); \ + TT_STMT_END + +/* Fail and abort the current test for the reason in msg */ +#define tt_abort_printf(msg) TT_DIE(msg) +#define tt_abort_perror(op) \ + TT_DIE(("%s: %s [%d]", (op), strerror(errno), errno)) +#define tt_abort_msg(msg) TT_DIE(("%s", msg)) +#define tt_abort() TT_DIE(("%s", "(Failed.)")) + +/* Fail but do not abort the current test for the reason in msg. */ +#define tt_fail_printf(msg) TT_FAIL(msg) +#define tt_fail_perror(op) \ + TT_FAIL(("%s: %s [%d]", (op), strerror(errno), errno)) +#define tt_fail_msg(msg) TT_FAIL(("%s", msg)) +#define tt_fail() TT_FAIL(("%s", "(Failed.)")) + +/* End the current test, and indicate we are skipping it. */ +#define tt_skip() \ + TT_STMT_BEGIN \ + tinytest_set_test_skipped_(); \ + TT_EXIT_TEST_FUNCTION; \ + TT_STMT_END + +#define tt_want_(b, msg, fail) \ + TT_STMT_BEGIN \ + if (!(b)) { \ + tinytest_set_test_failed_(); \ + TT_GRIPE(("%s", msg)); \ + fail; \ + } else { \ + TT_BLATHER(("%s", msg)); \ + } \ + TT_STMT_END + +/* Assert b, but do not stop the test if b fails. Log msg on failure. */ +#define tt_want_msg(b, msg) tt_want_(b, msg, ); + +/* Assert b and stop the test if b fails. Log msg on failure. */ +#define tt_assert_msg(b, msg) tt_want_(b, msg, TT_EXIT_TEST_FUNCTION); + +/* Assert b, but do not stop the test if b fails. */ +#define tt_want(b) tt_want_msg((b), "want(" #b ")") +/* Assert b, and stop the test if b fails. */ +#define tt_assert(b) tt_assert_msg((b), "assert(" #b ")") + +#define tt_assert_test_fmt_type(a, b, str_test, type, test, printf_type, \ + printf_fmt, setup_block, cleanup_block, \ + die_on_fail) \ + TT_STMT_BEGIN \ + type val1_ = (a); \ + type val2_ = (b); \ + int tt_status_ = (test); \ + if (!tt_status_ || tinytest_get_verbosity_() > 1) { \ + printf_type print_; \ + printf_type print1_; \ + printf_type print2_; \ + type value_ = val1_; \ + setup_block; \ + print1_ = print_; \ + value_ = val2_; \ + setup_block; \ + print2_ = print_; \ + TT_DECLARE(tt_status_ ? " OK" : "FAIL", \ + ("assert(%s): " printf_fmt " vs " printf_fmt, str_test, \ + print1_, print2_)); \ + print_ = print1_; \ + cleanup_block; \ + print_ = print2_; \ + cleanup_block; \ + if (!tt_status_) { \ + tinytest_set_test_failed_(); \ + die_on_fail; \ + } \ + } \ + TT_STMT_END + +#define tt_assert_test_type(a, b, str_test, type, test, fmt, die_on_fail) \ + tt_assert_test_fmt_type(a, b, str_test, type, test, type, fmt, \ + { print_ = value_; }, {}, die_on_fail) + +#define tt_assert_test_type_opt(a, b, str_test, type, test, fmt, die_on_fail) \ + tt_assert_test_fmt_type(a, b, str_test, type, test, type, fmt, \ + { print_ = value_ ? value_ : ""; }, {}, \ + die_on_fail) + +/* Helper: assert that a op b, when cast to type. Format the values with + * printf format fmt on failure. */ +#define tt_assert_op_type(a, op, b, type, fmt) \ + tt_assert_test_type(a, b, #a " " #op " " #b, type, (val1_ op val2_), fmt, \ + TT_EXIT_TEST_FUNCTION) + +#define tt_int_op(a, op, b) \ + tt_assert_test_type(a, b, #a " " #op " " #b, long, (val1_ op val2_), "%ld", \ + TT_EXIT_TEST_FUNCTION) + +#define tt_uint_op(a, op, b) \ + tt_assert_test_type(a, b, #a " " #op " " #b, unsigned long, \ + (val1_ op val2_), "%lu", TT_EXIT_TEST_FUNCTION) + +#define tt_ptr_op(a, op, b) \ + tt_assert_test_type(a, b, #a " " #op " " #b, const void *, (val1_ op val2_), \ + "%p", TT_EXIT_TEST_FUNCTION) + +#define tt_str_op(a, op, b) \ + tt_assert_test_type_opt(a, b, #a " " #op " " #b, const char *, \ + (val1_ && val2_ && strcmp(val1_, val2_) op 0), \ + "<%s>", TT_EXIT_TEST_FUNCTION) + +#define tt_mem_op(expr1, op, expr2, len) \ + tt_assert_test_fmt_type( \ + expr1, expr2, #expr1 " " #op " " #expr2, const void *, \ + (val1_ && val2_ && memcmp(val1_, val2_, len) op 0), char *, "%s", \ + { print_ = tinytest_format_hex_(value_, (len)); }, \ + { \ + if (print_) \ + free(print_); \ + }, \ + TT_EXIT_TEST_FUNCTION); + +#define tt_want_int_op(a, op, b) \ + tt_assert_test_type(a, b, #a " " #op " " #b, long, (val1_ op val2_), "%ld", \ + (void)0) + +#define tt_want_uint_op(a, op, b) \ + tt_assert_test_type(a, b, #a " " #op " " #b, unsigned long, \ + (val1_ op val2_), "%lu", (void)0) + +#define tt_want_ptr_op(a, op, b) \ + tt_assert_test_type(a, b, #a " " #op " " #b, const void *, (val1_ op val2_), \ + "%p", (void)0) + +#define tt_want_str_op(a, op, b) \ + tt_assert_test_type(a, b, #a " " #op " " #b, const char *, \ + (strcmp(val1_, val2_) op 0), "<%s>", (void)0) + +#define tt_want_mem_op(expr1, op, expr2, len) \ + tt_assert_test_fmt_type( \ + expr1, expr2, #expr1 " " #op " " #expr2, const void *, \ + (val1_ && val2_ && memcmp(val1_, val2_, len) op 0), char *, "%s", \ + { print_ = tinytest_format_hex_(value_, (len)); }, \ + { \ + if (print_) \ + free(print_); \ + }, \ + (void)0); + +#endif diff --git a/supportlibs/libsam3/src/libsam3/libsam3.c b/supportlibs/libsam3/src/libsam3/libsam3.c new file mode 100644 index 000000000..367949a39 --- /dev/null +++ b/supportlibs/libsam3/src/libsam3/libsam3.c @@ -0,0 +1,1304 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include "libsam3.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +int libsam3_debug = 0; + +//////////////////////////////////////////////////////////////////////////////// +/* convert struct timeval to milliseconds */ +/* +static inline uint64_t timeval2ms (const struct timeval *tv) { + return ((uint64_t)tv->tv_sec)*1000+((uint64_t)tv->tv_usec)/1000; +} +*/ + +/* convert milliseconds to timeval struct */ +static inline void ms2timeval(struct timeval *tv, uint64_t ms) { + tv->tv_sec = ms / 1000; + tv->tv_usec = (ms % 1000) * 1000; +} + +int sam3tcpSetTimeoutSend(int fd, int timeoutms) { + if (fd >= 0 && timeoutms >= 0) { + struct timeval tv; + // + ms2timeval(&tv, timeoutms); + return (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 ? -1 + : 0); + } + return -1; +} + +int sam3tcpSetTimeoutReceive(int fd, int timeoutms) { + if (fd >= 0 && timeoutms >= 0) { + struct timeval tv; + // + ms2timeval(&tv, timeoutms); + return (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ? -1 + : 0); + } + return -1; +} + +int sam3CheckValidKeyLength(const char *pubkey) { + if (strlen(pubkey) >= SAM3_PUBKEY_SIZE && + strlen(pubkey) <= SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE) { + return 1; + } + return 0; +} + +int sam3tcpConnectIP(uint32_t ip, int port) { + struct sockaddr_in addr; + int fd, val = 1; + char ipstr[18]; + // + if (ip == 0 || ip == 0xffffffffUL || port < 1 || port > 65535) + return -1; + // + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + if (libsam3_debug) + fprintf(stderr, "ERROR: can't create socket\n"); + return -1; + } + // + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + // + ipstr[0] = 0; + if (libsam3_debug) { + if (getnameinfo((struct sockaddr *)&addr, sizeof(struct sockaddr_in), ipstr, + sizeof(ipstr), NULL, 0, NI_NUMERICHOST) == 0) { + fprintf(stderr, "connecting to [%s:%d]...\n", ipstr, port); + } + } + // + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + // + if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { + if (libsam3_debug) + fprintf(stderr, "ERROR: can't connect\n"); + close(fd); + return -1; + } + // + if (libsam3_debug && ipstr[0]) + fprintf(stderr, "connected to [%s:%d]\n", ipstr, port); + // + return fd; +} + +/* returns fd or -1 */ +int sam3tcpConnect(const char *hostname, int port, uint32_t *ip) { + struct hostent *host = NULL; + // + if (hostname == NULL || !hostname[0] || port < 1 || port > 65535) + return -1; + // + host = gethostbyname(hostname); + if (host == NULL || host->h_name == NULL || !host->h_name[0]) { + if (libsam3_debug) + fprintf(stderr, "ERROR: can't resolve '%s'\n", hostname); + return -1; + } + // + if (libsam3_debug) { + char ipstr[18]; + // + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr = *((struct in_addr *)host->h_addr); + // + if (getnameinfo((struct sockaddr *)&addr, sizeof(struct sockaddr_in), ipstr, + sizeof(ipstr), NULL, 0, NI_NUMERICHOST) == 0) { + fprintf(stderr, "resolving: %s is [%s]...\n", hostname, ipstr); + } + } + // + if (ip != NULL) + *ip = ((struct in_addr *)host->h_addr)->s_addr; + return sam3tcpConnectIP(((struct in_addr *)host->h_addr)->s_addr, port); +} + +// <0: error; 0: ok +int sam3tcpDisconnect(int fd) { + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + return close(fd); + } + // + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +// <0: error; 0: ok +int sam3tcpSend(int fd, const void *buf, size_t bufSize) { + const char *c = (const char *)buf; + // + if (fd < 0 || (buf == NULL && bufSize > 0)) + return -1; + // + while (bufSize > 0) { + int wr = send(fd, c, bufSize, MSG_NOSIGNAL); + // + if (wr < 0 && errno == EINTR) + continue; // interrupted by signal + if (wr <= 0) + return -1; // either error or + c += wr; + bufSize -= wr; + } + // + return 0; +} + +/* <0: received (-res) bytes; read error */ +/* can return less that requesten bytes even if `allowPartial` is 0 when + * connection is closed */ +ssize_t sam3tcpReceiveEx(int fd, void *buf, size_t bufSize, int allowPartial) { + char *c = (char *)buf; + ssize_t total = 0; + // + if (fd < 0 || (buf == NULL && bufSize > 0)) + return -1; + // + while (bufSize > 0) { + int rd = recv(fd, c, bufSize, 0); + // + if (rd < 0 && errno == EINTR) + continue; // interrupted by signal + if (rd == 0) + return total; + if (rd < 0) + return -total; + c += rd; + total += rd; + bufSize -= rd; + if (allowPartial) + break; + } + // + return total; +} + +ssize_t sam3tcpReceive(int fd, void *buf, size_t bufSize) { + return sam3tcpReceiveEx(fd, buf, bufSize, 0); +} + +//////////////////////////////////////////////////////////////////////////////// +__attribute__((format(printf, 2, 3))) int sam3tcpPrintf(int fd, const char *fmt, + ...) { + int res; + char buf[1024], *p = buf; + int size = sizeof(buf) - 1; + // + for (;;) { + va_list ap; + char *np; + int n; + // + va_start(ap, fmt); + n = vsnprintf(p, size, fmt, ap); + va_end(ap); + // + if (n > -1 && n < size) + break; + if (n > -1) + size = n + 1; + else + size *= 2; + if (p == buf) { + if ((p = malloc(size + 4)) == NULL) + return -1; + } else { + if ((np = realloc(p, size + 4)) == NULL) { + free(p); + return -1; + } + p = np; + } + } + // + if (libsam3_debug) + fprintf(stderr, "SENDING: %s", p); + res = sam3tcpSend(fd, p, strlen(p)); + if (p != buf) + free(p); + return res; +} + +int sam3tcpReceiveStr(int fd, char *dest, size_t maxSize) { + char *d = dest; + // + if (maxSize < 1 || fd < 0 || dest == NULL) + return -1; + memset(dest, 0, maxSize); + while (maxSize > 1) { + char *e; + int rd = recv(fd, d, maxSize - 1, MSG_PEEK); + // + if (rd < 0 && errno == EINTR) + continue; // interrupted by signal + if (rd == 0) { + rd = recv(fd, d, 1, 0); + if (rd < 0 && errno == EINTR) + continue; // interrupted by signal + if (d[0] == '\n') { + d[0] = 0; // remove '\n' + return 0; + } + } else { + if (rd < 0) + return -1; // error or connection closed; alas + } + // check for EOL + d[maxSize - 1] = 0; + if ((e = strchr(d, '\n')) != NULL) { + rd = e - d + 1; // bytes to receive + if (sam3tcpReceive(fd, d, rd) < 0) + return -1; // alas + d[rd - 1] = 0; // remove '\n' + return 0; // done + } else { + // let's receive this part and go on + if (sam3tcpReceive(fd, d, rd) < 0) + return -1; // alas + maxSize -= rd; + d += rd; + } + } + // alas, the string is too big + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3udpSendToIP(uint32_t ip, int port, const void *buf, size_t bufSize) { + // TODO: ipv6 + struct sockaddr_in addr; + int fd, res; + // + if (buf == NULL || bufSize < 1) + return -1; + if (port < 1 || port > 65535) + port = 7655; + // + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + if (libsam3_debug) + fprintf(stderr, "ERROR: can't create socket\n"); + return -1; + } + // + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + // + res = sendto(fd, buf, bufSize, 0, (struct sockaddr *)&addr, sizeof(addr)); + // + if (res < 0) { + if (libsam3_debug) { + res = errno; + fprintf(stderr, "UDP ERROR (%d): %s\n", res, strerror(res)); + } + res = -1; + } else { + if (libsam3_debug) + fprintf(stderr, "UDP: %d bytes sent\n", res); + } + // + close(fd); + // + return (res >= 0 ? 0 : -1); +} + +int sam3udpSendTo(const char *hostname, int port, const void *buf, + size_t bufSize, uint32_t *ip) { + struct hostent *host = NULL; + // TODO: ipv6 + if (buf == NULL || bufSize < 1) + return -1; + if (hostname == NULL || !hostname[0]) + hostname = "localhost"; + if (port < 1 || port > 65535) + port = 7655; + // + host = gethostbyname(hostname); + if (host == NULL || host->h_name == NULL || !host->h_name[0]) { + if (libsam3_debug) + fprintf(stderr, "ERROR: can't resolve '%s'\n", hostname); + return -1; + } + // + if (ip != NULL) + *ip = ((struct in_addr *)host->h_addr)->s_addr; + return sam3udpSendToIP(((struct in_addr *)host->h_addr)->s_addr, port, buf, + bufSize); +} + +//////////////////////////////////////////////////////////////////////////////// +void sam3FreeFieldList(SAMFieldList *list) { + while (list != NULL) { + SAMFieldList *c = list; + // + list = list->next; + if (c->name != NULL) + free(c->name); + if (c->value != NULL) + free(c->value); + free(c); + } +} + +void sam3DumpFieldList(const SAMFieldList *list) { + for (; list != NULL; list = list->next) { + fprintf(stderr, "%s=[%s]\n", list->name, list->value); + } +} + +const char *sam3FindField(const SAMFieldList *list, const char *field) { + if (list != NULL && field != NULL) { + for (list = list->next; list != NULL; list = list->next) { + if (list->name != NULL && strcmp(field, list->name) == 0) + return list->value; + } + } + return NULL; +} + +static char *xstrdup(const char *s, int len) { + if (len >= 0) { + char *res = malloc(len + 1); + // + if (res != NULL) { + if (len > 0) + memcpy(res, s, len); + res[len] = 0; + } + // + return res; + } + // + return NULL; +} + +// returns NULL if there are no more tokens +static inline const char *xstrtokend(const char *s) { + while (*s && isspace(*s)) + ++s; + // + if (*s) { + char qch = 0; + // + while (*s) { + if (*s == qch) { + qch = 0; + } else if (*s == '"') { + qch = *s; + } else if (qch) { + if (*s == '\\' && s[1]) + ++s; + } else if (isspace(*s)) { + break; + } + ++s; + } + } else { + s = NULL; + } + // + return s; +} + +SAMFieldList *sam3ParseReply(const char *rep) { + SAMFieldList *first = NULL, *last, *c; + const char *p = rep, *e, *e1; + // + // first 2 words + while (*p && isspace(*p)) + ++p; + if ((e = xstrtokend(p)) == NULL) + return NULL; + if ((e1 = xstrtokend(e)) == NULL) + return NULL; + // + if ((first = last = c = malloc(sizeof(SAMFieldList))) == NULL) + return NULL; + c->next = NULL; + c->name = c->value = NULL; + if ((c->name = xstrdup(p, e - p)) == NULL) + goto error; + while (*e && isspace(*e)) + ++e; + if ((c->value = xstrdup(e, e1 - e)) == NULL) + goto error; + // + p = e1; + while (*p) { + while (*p && isspace(*p)) + ++p; + if ((e = xstrtokend(p)) == NULL) + break; // no more tokens + // + if (libsam3_debug) + fprintf(stderr, "<%s>\n", p); + // + if ((c = malloc(sizeof(SAMFieldList))) == NULL) + return NULL; + c->next = NULL; + c->name = c->value = NULL; + last->next = c; + last = c; + // + if ((e1 = memchr(p, '=', e - p)) != NULL) { + // key=value + if ((c->name = xstrdup(p, e1 - p)) == NULL) + goto error; + if ((c->value = xstrdup(e1 + 1, e - e1 - 1)) == NULL) + goto error; + } else { + // only key (there is no such replies in SAMv3, but... + if ((c->name = xstrdup(p, e - p)) == NULL) + goto error; + if ((c->value = strdup("")) == NULL) + goto error; + } + p = e; + } + // + if (libsam3_debug) + sam3DumpFieldList(first); + // + return first; +error: + sam3FreeFieldList(first); + return NULL; +} + +// NULL: error; else: list of fields +// first item is always 2-word reply, with first word in name and second in +// value +SAMFieldList *sam3ReadReply(int fd) { + char rep[2048]; // should be enough for any reply + // + if (sam3tcpReceiveStr(fd, rep, sizeof(rep)) < 0) + return NULL; + if (libsam3_debug) + fprintf(stderr, "SAM REPLY: [%s]\n", rep); + return sam3ParseReply(rep); +} + +// example: +// r0: 'HELLO' +// r1: 'REPLY' +// field: NULL or 'RESULT' +// VALUE: NULL or 'OK' +// returns bool +int sam3IsGoodReply(const SAMFieldList *list, const char *r0, const char *r1, + const char *field, const char *value) { + if (list != NULL && list->name != NULL && list->value != NULL) { + if (r0 != NULL && strcmp(r0, list->name) != 0) + return 0; + if (r1 != NULL && strcmp(r1, list->value) != 0) + return 0; + if (field != NULL) { + for (list = list->next; list != NULL; list = list->next) { + if (list->name == NULL || list->value == NULL) + return 0; // invalid list, heh + if (strcmp(field, list->name) == 0) { + if (value != NULL && strcmp(value, list->value) != 0) + return 0; + return 1; + } + } + } + return 1; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// by Bob Jenkins +// public domain +// http://burtleburtle.net/bob/rand/smallprng.html +// +//////////////////////////////////////////////////////////////////////////////// +typedef struct { + uint32_t a, b, c, d; +} BJRandCtx; + +#define BJPRNG_ROT(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) + +static uint32_t bjprngRand(BJRandCtx *x) { + uint32_t e; + /* original: + e = x->a-BJPRNG_ROT(x->b, 27); + x->a = x->b^BJPRNG_ROT(x->c, 17); + x->b = x->c+x->d; + x->c = x->d+e; + x->d = e+x->a; + */ + /* better, but slower at least in idiotic m$vc */ + e = x->a - BJPRNG_ROT(x->b, 23); + x->a = x->b ^ BJPRNG_ROT(x->c, 16); + x->b = x->c + BJPRNG_ROT(x->d, 11); + x->c = x->d + e; + x->d = e + x->a; + // + return x->d; +} + +static void bjprngInit(BJRandCtx *x, uint32_t seed) { + x->a = 0xf1ea5eed; + x->b = x->c = x->d = seed; + for (int i = 0; i < 20; ++i) + bjprngRand(x); +} + +static inline uint32_t hashint(uint32_t a) { + a -= (a << 6); + a ^= (a >> 17); + a -= (a << 9); + a ^= (a << 4); + a -= (a << 3); + a ^= (a << 10); + a ^= (a >> 15); + return a; +} + +static uint32_t genSeed(void) { + uint32_t res; +#ifndef WIN32 + struct sysinfo sy; + pid_t pid = getpid(); + // + sysinfo(&sy); + res = hashint((uint32_t)pid) ^ hashint((uint32_t)time(NULL)) ^ + hashint((uint32_t)sy.sharedram) ^ hashint((uint32_t)sy.bufferram) ^ + hashint((uint32_t)sy.uptime); +#else + res = hashint((uint32_t)GetCurrentProcessId()) ^ + hashint((uint32_t)GetTickCount()); +#endif + return hashint(res); +} + +//////////////////////////////////////////////////////////////////////////////// +size_t sam3GenChannelName(char *dest, size_t minlen, size_t maxlen) { + BJRandCtx rc; + size_t len; + size_t retlen; + // + if (dest == NULL || minlen < 1 || maxlen < minlen || minlen > 65536 || + maxlen > 65536) + return -1; + bjprngInit(&rc, genSeed()); + len = minlen + (bjprngRand(&rc) % (maxlen - minlen + 1)); + retlen = len; + while (len--) { + int ch = bjprngRand(&rc) % 64; + // + if (ch >= 0 && ch < 10) + ch += '0'; + else if (ch >= 10 && ch < 36) + ch += 'A' - 10; + else if (ch >= 36 && ch < 62) + ch += 'a' - 36; + else if (ch == 62) + ch = '-'; + else if (ch == 63) + ch = '_'; + else if (ch > 64) + abort(); + *dest++ = ch; + } + *dest++ = 0; + return retlen; +} + +//////////////////////////////////////////////////////////////////////////////// +static int sam3HandshakeInternal(int fd) { + SAMFieldList *rep = NULL; + // + if (sam3tcpPrintf(fd, "HELLO VERSION MIN=3.0 MAX=3.1\n") < 0) + goto error; + rep = sam3ReadReply(fd); + if (!sam3IsGoodReply(rep, "HELLO", "REPLY", "RESULT", "OK")) + goto error; + sam3FreeFieldList(rep); + return fd; +error: + sam3tcpDisconnect(fd); + if (rep != NULL) + sam3FreeFieldList(rep); + return -1; +} + +int sam3HandshakeIP(uint32_t ip, int port) { + int fd; + // + if ((fd = sam3tcpConnectIP(ip, (port < 1 || port > 65535 ? 7656 : port))) < 0) + return -1; + return sam3HandshakeInternal(fd); +} + +int sam3Handshake(const char *hostname, int port, uint32_t *ip) { + int fd; + // + if ((fd = sam3tcpConnect( + (hostname == NULL || !hostname[0] ? "localhost" : hostname), + (port < 1 || port > 65535 ? 7656 : port), ip)) < 0) + return -1; + return sam3HandshakeInternal(fd); +} + +//////////////////////////////////////////////////////////////////////////////// +static inline void strcpyerr(Sam3Session *ses, const char *errstr) { + memset(ses->error, 0, sizeof(ses->error)); + if (errstr != NULL) + strncpy(ses->error, errstr, sizeof(ses->error) - 1); +} + +int sam3GenerateKeys(Sam3Session *ses, const char *hostname, int port, + int sigType) { + if (ses != NULL) { + SAMFieldList *rep = NULL; + int fd, res = -1; + static const char *sigtypes[5] = { + "SIGNATURE_TYPE=DSA_SHA1", "SIGNATURE_TYPE=ECDSA_SHA256_P256", + "SIGNATURE_TYPE=ECDSA_SHA384_P384", "SIGNATURE_TYPE=ECDSA_SHA512_P521", + "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"}; + // + if ((fd = sam3Handshake(hostname, port, NULL)) < 0) { + strcpyerr(ses, "I2P_ERROR"); + return -1; + } + // + if (sam3tcpPrintf(fd, "DEST GENERATE %s\n", sigtypes[sigType]) >= 0) { + if ((rep = sam3ReadReply(fd)) != NULL && + sam3IsGoodReply(rep, "DEST", "REPLY", NULL, NULL)) { + const char *pub = sam3FindField(rep, "PUB"), + *priv = sam3FindField(rep, "PRIV"); + // + if (pub != NULL && sam3CheckValidKeyLength(pub) && priv != NULL && + strlen(priv) >= SAM3_PRIVKEY_MIN_SIZE) { + strcpy(ses->pubkey, pub); + strcpy(ses->privkey, priv); + res = 0; + } + } + } + // + sam3FreeFieldList(rep); + sam3tcpDisconnect(fd); + // + return res; + } + return -1; +} + +int sam3NameLookup(Sam3Session *ses, const char *hostname, int port, + const char *name) { + if (ses != NULL && name != NULL && name[0]) { + SAMFieldList *rep = NULL; + int fd, res = -1; + // + if ((fd = sam3Handshake(hostname, port, NULL)) < 0) { + strcpyerr(ses, "I2P_ERROR"); + return -1; + } + // + strcpyerr(ses, "I2P_ERROR"); + if (sam3tcpPrintf(fd, "NAMING LOOKUP NAME=%s\n", name) >= 0) { + if ((rep = sam3ReadReply(fd)) != NULL && + sam3IsGoodReply(rep, "NAMING", "REPLY", "RESULT", NULL)) { + const char *rs = sam3FindField(rep, "RESULT"), + *pub = sam3FindField(rep, "VALUE"); + // + if (strcmp(rs, "OK") == 0) { + if (pub != NULL && sam3CheckValidKeyLength(pub)) { + strcpy(ses->destkey, pub); + strcpyerr(ses, NULL); + res = 0; + } + } else if (rs[0]) { + strcpyerr(ses, rs); + } + } + } + // + sam3FreeFieldList(rep); + sam3tcpDisconnect(fd); + // + return res; + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +static int sam3CloseConnectionInternal(Sam3Connection *conn) { + return (conn->fd >= 0 ? sam3tcpDisconnect(conn->fd) : 0); +} + +int sam3CloseConnection(Sam3Connection *conn) { + if (conn != NULL) { + int res = sam3CloseConnectionInternal(conn); + // + if (conn->ses != NULL) { + for (Sam3Connection *p = NULL, *c = conn->ses->connlist; c != NULL; + p = c, c = c->next) { + if (c == conn) { + if (p == NULL) + conn->ses->connlist = c->next; + else + p->next = c->next; + break; + } + } + } + free(conn); + // + return res; + } + return -1; +} + +int sam3CloseSession(Sam3Session *ses) { + if (ses != NULL) { + for (Sam3Connection *n, *c = ses->connlist; c != NULL; c = n) { + n = c->next; + sam3CloseConnectionInternal(c); + free(c); + } + if (ses->fwd_fd >= 0) + sam3tcpDisconnect(ses->fwd_fd); + if (ses->fd >= 0) + sam3tcpDisconnect(ses->fd); + memset(ses, 0, sizeof(Sam3Session)); + ses->fd = -1; + return 0; + } + return -1; +} + +int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, + const char *privkey, Sam3SessionType type, + Sam3SigType sigType, const char *params) { + if (ses != NULL) { + static const char *typenames[3] = {"RAW", "DATAGRAM", "STREAM"}; + static const char *sigtypes[5] = { + "SIGNATURE_TYPE=DSA_SHA1", "SIGNATURE_TYPE=ECDSA_SHA256_P256", + "SIGNATURE_TYPE=ECDSA_SHA384_P384", "SIGNATURE_TYPE=ECDSA_SHA512_P521", + "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"}; + + SAMFieldList *rep; + const char *v = NULL; + const char *pdel = (params != NULL ? " " : ""); + // + memset(ses, 0, sizeof(Sam3Session)); + ses->fd = -1; + ses->fwd_fd = -1; + // + if (privkey != NULL && strlen(privkey) < SAM3_PRIVKEY_MIN_SIZE) + goto error; + if ((int)type < 0 || (int)type > 2) + goto error; + if (privkey == NULL) + privkey = "TRANSIENT"; + // + ses->type = type; + ses->sigType = sigType; + ses->port = (type == SAM3_SESSION_STREAM ? (port ? port : 7656) : 7655); + sam3GenChannelName(ses->channel, 32, 64); + if (libsam3_debug) + fprintf(stderr, "sam3CreateSession: channel=[%s]\n", ses->channel); + // + if ((ses->fd = sam3Handshake(hostname, port, &ses->ip)) < 0) + goto error; + // + if (libsam3_debug) + fprintf(stderr, "sam3CreateSession: creating session (%s)...\n", + typenames[(int)type]); + if (sam3tcpPrintf( + ses->fd, "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s %s %s %s\n", + typenames[(int)type], ses->channel, privkey, sigtypes[(int)sigType], + pdel, (params != NULL ? params : "")) < 0) + goto error; + if ((rep = sam3ReadReply(ses->fd)) == NULL) + goto error; + if (!sam3IsGoodReply(rep, "SESSION", "STATUS", "RESULT", "OK") || + (v = sam3FindField(rep, "DESTINATION")) == NULL || + strlen(v) < SAM3_PRIVKEY_MIN_SIZE) { + if (libsam3_debug) + fprintf(stderr, "sam3CreateSession: invalid reply (%ld)...\n", + (v != NULL ? strlen(v) : -1)); + if (libsam3_debug) + sam3DumpFieldList(rep); + sam3FreeFieldList(rep); + goto error; + } + // save our keys + strcpy(ses->privkey, v); + sam3FreeFieldList(rep); + // get public key + if (sam3tcpPrintf(ses->fd, "NAMING LOOKUP NAME=ME\n") < 0) + goto error; + if ((rep = sam3ReadReply(ses->fd)) == NULL) + goto error; + v = NULL; + if (!sam3IsGoodReply(rep, "NAMING", "REPLY", "RESULT", "OK") || + (v = sam3FindField(rep, "VALUE")) == NULL || + !sam3CheckValidKeyLength(v)) { + if (libsam3_debug) + fprintf(stderr, "sam3CreateSession: invalid NAMING reply (%ld)...\n", + (v != NULL ? strlen(v) : -1)); + if (libsam3_debug) + sam3DumpFieldList(rep); + sam3FreeFieldList(rep); + goto error; + } + strcpy(ses->pubkey, v); + sam3FreeFieldList(rep); + // + if (libsam3_debug) + fprintf(stderr, "sam3CreateSession: complete.\n"); + return 0; + } +error: + sam3CloseSession(ses); + return -1; +} + +Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey) { + if (ses != NULL) { + SAMFieldList *rep; + Sam3Connection *conn; + // + if (ses->type != SAM3_SESSION_STREAM) { + strcpyerr(ses, "INVALID_SESSION_TYPE"); + return NULL; + } + if (ses->fd < 0) { + strcpyerr(ses, "INVALID_SESSION"); + return NULL; + } + if (destkey == NULL || !sam3CheckValidKeyLength(destkey)) { + strcpyerr(ses, "INVALID_KEY"); + return NULL; + } + if ((conn = calloc(1, sizeof(Sam3Connection))) == NULL) { + strcpyerr(ses, "NO_MEMORY"); + return NULL; + } + if ((conn->fd = sam3HandshakeIP(ses->ip, ses->port)) < 0) { + strcpyerr(ses, "IO_ERROR_SK"); + goto error; + } + if (sam3tcpPrintf(conn->fd, "STREAM CONNECT ID=%s DESTINATION=%s\n", + ses->channel, destkey) < 0) { + strcpyerr(ses, "IO_ERROR"); + goto error; + } + if ((rep = sam3ReadReply(conn->fd)) == NULL) { + strcpyerr(ses, "IO_ERROR"); + goto error; + } + if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { + const char *v = sam3FindField(rep, "RESULT"); + // + strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR")); + sam3CloseConnectionInternal(conn); + free(conn); + conn = NULL; + } else { + // no error + strcpyerr(ses, NULL); + } + sam3FreeFieldList(rep); + if (conn != NULL) { + strcpy(conn->destkey, destkey); + conn->ses = ses; + conn->next = ses->connlist; + ses->connlist = conn; + } + return conn; + error: + sam3CloseConnectionInternal(conn); + free(conn); + return NULL; + } + return NULL; +} + +Sam3Connection *sam3StreamAccept(Sam3Session *ses) { + if (ses != NULL) { + SAMFieldList *rep = NULL; + char repstr[1024]; + Sam3Connection *conn; + // + if (ses->type != SAM3_SESSION_STREAM) { + strcpyerr(ses, "INVALID_SESSION_TYPE"); + return NULL; + } + if (ses->fd < 0) { + strcpyerr(ses, "INVALID_SESSION"); + return NULL; + } + if ((conn = calloc(1, sizeof(Sam3Connection))) == NULL) { + strcpyerr(ses, "NO_MEMORY"); + return NULL; + } + if ((conn->fd = sam3HandshakeIP(ses->ip, ses->port)) < 0) { + strcpyerr(ses, "IO_ERROR_SK"); + goto error; + } + if (sam3tcpPrintf(conn->fd, "STREAM ACCEPT ID=%s\n", ses->channel) < 0) { + strcpyerr(ses, "IO_ERROR_PF"); + goto error; + } + if ((rep = sam3ReadReply(conn->fd)) == NULL) { + strcpyerr(ses, "IO_ERROR_RP"); + goto error; + } + if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { + const char *v = sam3FindField(rep, "RESULT"); + // + strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES")); + goto error; + } + if (sam3tcpReceiveStr(conn->fd, repstr, sizeof(repstr)) < 0) { + strcpyerr(ses, "IO_ERROR_RP1"); + goto error; + } + sam3FreeFieldList(rep); + if ((rep = sam3ParseReply(repstr)) != NULL) { + const char *v = sam3FindField(rep, "RESULT"); + // + strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES1")); + goto error; + } + if (!sam3CheckValidKeyLength(repstr)) { + strcpyerr(ses, "INVALID_KEY"); + goto error; + } + sam3FreeFieldList(rep); + strcpy(conn->destkey, repstr); + conn->ses = ses; + conn->next = ses->connlist; + ses->connlist = conn; + strcpyerr(ses, NULL); + return conn; + error: + if (rep != NULL) + sam3FreeFieldList(rep); + sam3CloseConnectionInternal(conn); + free(conn); + return NULL; + } + return NULL; +} + +int sam3StreamForward(Sam3Session *ses, const char *hostname, int port) { + if (ses != NULL) { + SAMFieldList *rep = NULL; + // + if (ses->type != SAM3_SESSION_STREAM) { + strcpyerr(ses, "INVALID_SESSION_TYPE"); + return -1; + } + if (ses->fd < 0) { + strcpyerr(ses, "INVALID_SESSION"); + return -1; + } + if (ses->fwd_fd >= 0) { + strcpyerr(ses, "DUPLICATE_FORWARD"); + return -1; + } + if ((ses->fwd_fd = sam3HandshakeIP(ses->ip, ses->port)) < 0) { + strcpyerr(ses, "IO_ERROR_SK"); + goto error; + } + if (sam3tcpPrintf(ses->fwd_fd, "STREAM FORWARD ID=%s PORT=%d HOST=%s SILENT=true\n", + ses->channel, port, hostname) < 0) { + strcpyerr(ses, "IO_ERROR_PF"); + goto error; + } + if ((rep = sam3ReadReply(ses->fwd_fd)) == NULL) { + strcpyerr(ses, "IO_ERROR_RP"); + goto error; + } + if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { + const char *v = sam3FindField(rep, "RESULT"); + // + strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES")); + goto error; + } + sam3FreeFieldList(rep); + strcpyerr(ses, NULL); + return 0; + error: + if (rep != NULL) + sam3FreeFieldList(rep); + return -1; + } + return -1; +} + +int sam3DatagramSend(Sam3Session *ses, const char *destkey, const void *buf, + size_t bufsize) { + if (ses != NULL) { + char *dbuf; + int res, dbufsz; + // + if (ses->type == SAM3_SESSION_STREAM) { + strcpyerr(ses, "INVALID_SESSION_TYPE"); + return -1; + } + if (ses->fd < 0) { + strcpyerr(ses, "INVALID_SESSION"); + return -1; + } + if (destkey == NULL || !sam3CheckValidKeyLength(destkey)) { + strcpyerr(ses, "INVALID_KEY"); + return -1; + } + if (buf == NULL || bufsize < 1 || bufsize > 31744) { + strcpyerr(ses, "INVALID_DATA"); + return -1; + } + dbufsz = bufsize + 4 + SAM3_PUBKEY_SIZE + 1 + strlen(ses->channel) + 1; + if ((dbuf = malloc(dbufsz)) == NULL) { + strcpyerr(ses, "OUT_OF_MEMORY"); + return -1; + } + sprintf(dbuf, "3.0 %s %s\n", ses->channel, destkey); + memcpy(dbuf + strlen(dbuf), buf, bufsize); + res = sam3udpSendToIP(ses->ip, ses->port, dbuf, dbufsz); + free(dbuf); + strcpyerr(ses, (res < 0 ? "IO_ERROR" : NULL)); + return (res < 0 ? -1 : 0); + } + return -1; +} + +ssize_t sam3DatagramReceive(Sam3Session *ses, void *buf, size_t bufsize) { + if (ses != NULL) { + SAMFieldList *rep; + const char *v; + ssize_t size = 0; + // + if (ses->type == SAM3_SESSION_STREAM) { + strcpyerr(ses, "INVALID_SESSION_TYPE"); + return -1; + } + if (ses->fd < 0) { + strcpyerr(ses, "INVALID_SESSION"); + return -1; + } + if (buf == NULL || bufsize < 1) { + strcpyerr(ses, "INVALID_BUFFER"); + return -1; + } + if ((rep = sam3ReadReply(ses->fd)) == NULL) { + strcpyerr(ses, "IO_ERROR"); + return -1; + } + if (!sam3IsGoodReply(rep, "DATAGRAM", "RECEIVED", "SIZE", NULL)) { + strcpyerr(ses, "I2P_ERROR"); + sam3FreeFieldList(rep); + return -1; + } + // + if ((v = sam3FindField(rep, "DESTINATION")) != NULL && + sam3CheckValidKeyLength(v)) + strncpy(ses->destkey, v, sizeof(ses->destkey)); + v = sam3FindField(rep, "SIZE"); // we have this field -- for sure + if (!v[0] || !isdigit(*v)) { + strcpyerr(ses, "I2P_ERROR_SIZE"); + sam3FreeFieldList(rep); + return -1; + } + // + while (*v && isdigit(*v)) { + if ((size = size * 10 + v[0] - '0') > bufsize) { + strcpyerr(ses, "I2P_ERROR_BUFFER_TOO_SMALL"); + sam3FreeFieldList(rep); + return -1; + } + ++v; + } + // + if (*v) { + strcpyerr(ses, "I2P_ERROR_SIZE"); + sam3FreeFieldList(rep); + return -1; + } + sam3FreeFieldList(rep); + // + if (sam3tcpReceive(ses->fd, buf, size) != size) { + strcpyerr(ses, "IO_ERROR"); + return -1; + } + strcpyerr(ses, NULL); + return size; + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +// output 8 bytes for every 5 input +// return size or <0 on error +ssize_t sam3Base32Encode(char *dest, size_t destsz, const void *srcbuf, + size_t srcsize) { + if (dest != NULL && srcbuf != NULL && srcsize >= 0) { + static const char *const b32chars = "abcdefghijklmnopqrstuvwxyz234567="; + const unsigned char *src = (const unsigned char *)srcbuf; + ssize_t destsize = 0; + // + while (srcsize > 0) { + int blksize = (srcsize < 5 ? srcsize : 5); + unsigned char n[8]; + // + memset(n, 0, sizeof(n)); + switch (blksize) { + case 5: + n[7] = (src[4] & 0x1f); + n[6] = ((src[4] & 0xe0) >> 5); + case 4: + n[6] |= ((src[3] & 0x03) << 3); + n[5] = ((src[3] & 0x7c) >> 2); + n[4] = ((src[3] & 0x80) >> 7); + case 3: + n[4] |= ((src[2] & 0x0f) << 1); + n[3] = ((src[2] & 0xf0) >> 4); + case 2: + n[3] |= ((src[1] & 0x01) << 4); + n[2] = ((src[1] & 0x3e) >> 1); + n[1] = ((src[1] & 0xc0) >> 6); + case 1: + n[1] |= ((src[0] & 0x07) << 2); + n[0] = ((src[0] & 0xf8) >> 3); + break; + } + src += blksize; + srcsize -= blksize; + // pad + switch (blksize) { + case 1: + n[2] = n[3] = 32; + case 2: + n[4] = 32; + case 3: + n[5] = n[6] = 32; + case 4: + n[7] = 32; + case 5: + break; + } + // output + if (destsize + 8 <= destsz) { + for (int f = 0; f < 8; ++f) + *dest++ = b32chars[n[f]]; + } + destsize += 8; + } + if (destsize <= destsz) { + *dest++ = 0; // make valid asciiz string + } + return destsize; + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +// output 8 bytes for every 5 input +// return size or <0 on error +ssize_t sam3Base32Decode(char *dest, size_t destsz, const void *srcbuf, + size_t srcsize) { + if (dest != NULL && srcbuf != NULL && srcsize >= 0) { + static const char *const b32chars = "abcdefghijklmnopqrstuvwxyz234567="; + const unsigned char *src = (const unsigned char *)srcbuf; + int destsize = 0; + // + while (srcsize > 0) { + int blksize = (srcsize < 5 ? srcsize : 5); + unsigned char n[8]; + // + memset(n, 0, sizeof(n)); + switch (blksize) { + case 5: + n[7] = (src[4] & 0x1f); + n[6] = ((src[4] & 0xe0) >> 5); + case 4: + n[6] |= ((src[3] & 0x03) << 3); + n[5] = ((src[3] & 0x7c) >> 2); + n[4] = ((src[3] & 0x80) >> 7); + case 3: + n[4] |= ((src[2] & 0x0f) << 1); + n[3] = ((src[2] & 0xf0) >> 4); + case 2: + n[3] |= ((src[1] & 0x01) << 4); + n[2] = ((src[1] & 0x3e) >> 1); + n[1] = ((src[1] & 0xc0) >> 6); + case 1: + n[1] |= ((src[0] & 0x07) << 2); + n[0] = ((src[0] & 0xf8) >> 3); + break; + } + src += blksize; + srcsize -= blksize; + // pad + switch (blksize) { + case 1: + n[2] = n[3] = 32; + case 2: + n[4] = 32; + case 3: + n[5] = n[6] = 32; + case 4: + n[7] = 32; + case 5: + break; + } + // output + if (destsize + 8 <= destsz) { + for (int f = 0; f < 8; ++f) + *dest++ = b32chars[n[f]]; + } + destsize += 8; + } + if (destsize <= destsz) { + *dest++ = 0; // make valid asciiz string + } + return destsize; + } + return -1; +} diff --git a/supportlibs/libsam3/src/libsam3/libsam3.h b/supportlibs/libsam3/src/libsam3/libsam3.h new file mode 100644 index 000000000..3eb261d89 --- /dev/null +++ b/supportlibs/libsam3/src/libsam3/libsam3.h @@ -0,0 +1,280 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#ifndef LIBSAM3_H +#define LIBSAM3_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////// +extern int libsam3_debug; + +//////////////////////////////////////////////////////////////////////////////// +#define SAM3_HOST_DEFAULT (NULL) +#define SAM3_PORT_DEFAULT (0) + +#define SAM3_DESTINATION_TRANSIENT (NULL) + +#define SAM3_PUBKEY_SIZE (516) +#define SAM3_CERT_SIZE (100) +#define SAM3_PRIVKEY_MIN_SIZE (884) + +//////////////////////////////////////////////////////////////////////////////// +/* returns fd or -1 */ +/* 'ip': host IP; can be NULL */ +extern int sam3tcpConnect(const char *hostname, int port, uint32_t *ip); +extern int sam3tcpConnectIP(uint32_t ip, int port); + +/* <0: error; 0: ok */ +extern int sam3tcpDisconnect(int fd); + +/* <0: error; 0: ok */ +extern int sam3tcpSetTimeoutSend(int fd, int timeoutms); + +/* <0: error; 0: ok */ +extern int sam3tcpSetTimeoutReceive(int fd, int timeoutms); + +/* <0: error; 0: ok */ +/* sends the whole buffer */ +extern int sam3tcpSend(int fd, const void *buf, size_t bufSize); + +/* <0: received (-res) bytes; read error */ +/* can return less that requesten bytes even if `allowPartial` is 0 when + * connection is closed */ +extern ssize_t sam3tcpReceiveEx(int fd, void *buf, size_t bufSize, + int allowPartial); + +extern ssize_t sam3tcpReceive(int fd, void *buf, size_t bufSize); + +extern int sam3tcpPrintf(int fd, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +extern int sam3tcpReceiveStr(int fd, char *dest, size_t maxSize); + +/* pass NULL for 'localhost' and 0 for 7655 */ +/* 'ip': host IP; can be NULL */ +extern int sam3udpSendTo(const char *hostname, int port, const void *buf, + size_t bufSize, uint32_t *ip); +extern int sam3udpSendToIP(uint32_t ip, int port, const void *buf, + size_t bufSize); + +//////////////////////////////////////////////////////////////////////////////// +typedef struct SAMFieldList { + char *name; + char *value; + struct SAMFieldList *next; +} SAMFieldList; + +extern void sam3FreeFieldList(SAMFieldList *list); +extern void sam3DumpFieldList(const SAMFieldList *list); + +/* read and parse SAM reply */ +/* NULL: error; else: list of fields */ +/* first item is always 2-word reply, with first word in name and second in + * value */ +extern SAMFieldList *sam3ReadReply(int fd); + +extern SAMFieldList *sam3ParseReply(const char *rep); + +/* + * example: + * r0: 'HELLO' + * r1: 'REPLY' + * field: NULL or 'RESULT' + * VALUE: NULL or 'OK' + * returns bool + */ +extern int sam3IsGoodReply(const SAMFieldList *list, const char *r0, + const char *r1, const char *field, + const char *value); + +extern const char *sam3FindField(const SAMFieldList *list, const char *field); + +//////////////////////////////////////////////////////////////////////////////// +/* pass NULL for 'localhost' and 0 for 7656 */ +/* returns <0 on error or socket fd on success */ +extern int sam3Handshake(const char *hostname, int port, uint32_t *ip); +extern int sam3HandshakeIP(uint32_t ip, int port); + +//////////////////////////////////////////////////////////////////////////////// +typedef enum { + SAM3_SESSION_RAW, + SAM3_SESSION_DGRAM, + SAM3_SESSION_STREAM +} Sam3SessionType; + +typedef enum { + DSA_SHA1, + ECDSA_SHA256_P256, + ECDSA_SHA384_P384, + ECDSA_SHA512_P521, + EdDSA_SHA512_Ed25519 +} Sam3SigType; + +typedef struct Sam3Session { + Sam3SessionType type; + Sam3SigType sigType; + int fd; + char privkey[SAM3_PRIVKEY_MIN_SIZE + 1]; // destination private key (asciiz) + char pubkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + + 1]; // destination public key (asciiz) + char channel[66]; // name of this sam session (asciiz) + char destkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + + 1]; // for DGRAM sessions (asciiz) + // int destsig; + char error[32]; // error message (asciiz) + uint32_t ip; + int port; // this will be changed to UDP port for DRAM/RAW (can be 0) + struct Sam3Connection *connlist; // list of opened connections + int fwd_fd; +} Sam3Session; + +typedef struct Sam3Connection { + Sam3Session *ses; + struct Sam3Connection *next; + int fd; + char destkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + + 1]; // remote destination public key (asciiz) + int destcert; + char error[32]; // error message (asciiz) +} Sam3Connection; + +//////////////////////////////////////////////////////////////////////////////// +/* + * create SAM session + * pass NULL as hostname for 'localhost' and 0 as port for 7656 + * pass NULL as privkey to create TRANSIENT session + * 'params' can be NULL + * see http://www.i2p2.i2p/i2cp.html#options for common options, + * and http://www.i2p2.i2p/streaming.html#options for STREAM options + * if result<0: error, 'ses' fields are undefined, no need to call + * sam3CloseSession() if result==0: ok, all 'ses' fields are filled + * TODO: don't clear 'error' field on error (and set it to something meaningful) + */ +extern int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, + const char *privkey, Sam3SessionType type, + Sam3SigType sigType, const char *params); + +/* + * close SAM session (and all it's connections) + * returns <0 on error, 0 on ok + * 'ses' must be properly initialized + */ +extern int sam3CloseSession(Sam3Session *ses); + +/* + * Check to make sure that the destination in use is of a valid length, returns + * 1 if true and 0 if false. + */ +int sam3CheckValidKeyLength(const char *pubkey); + +/* + * open stream connection to 'destkey' endpoint + * 'destkey' is 516-byte public key (asciiz) + * returns <0 on error, fd on ok + * you still have to call sam3CloseSession() on failure + * sets ses->error on error + */ +extern Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey); + +/* + * accepts stream connection and sets 'destkey' + * 'destkey' is 516-byte public key + * returns <0 on error, fd on ok + * you still have to call sam3CloseSession() on failure + * sets ses->error on error + * note that there is no timeouts for now, but you can use sam3tcpSetTimeout*() + */ +extern Sam3Connection *sam3StreamAccept(Sam3Session *ses); + +/* + * sets up forwarding stream connection + * returns <0 on error, 0 on ok + * you still have to call sam3CloseSession() on failure + * sets ses->error on error + * note that there is no timeouts for now, but you can use sam3tcpSetTimeout*() + */ +extern int sam3StreamForward(Sam3Session *ses, const char *hostname, int port); + +/* + * close SAM connection + * returns <0 on error, 0 on ok + * 'conn' must be properly initialized + * 'conn' is invalid after call + */ +extern int sam3CloseConnection(Sam3Connection *conn); + +//////////////////////////////////////////////////////////////////////////////// +/* + * generate new keypair + * fills 'privkey' and 'pubkey' only + * you should not call sam3CloseSession() on 'ses' + * will not set 'error' field + * returns <0 on error, 0 on ok + */ +extern int sam3GenerateKeys(Sam3Session *ses, const char *hostname, int port, + int sigType); + +/* + * do name lookup (something like gethostbyname()) + * fills 'destkey' only + * you should not call sam3CloseSession() on 'ses' + * will set 'error' field + * returns <0 on error, 0 on ok + */ +extern int sam3NameLookup(Sam3Session *ses, const char *hostname, int port, + const char *name); + +//////////////////////////////////////////////////////////////////////////////// +/* + * sends datagram to 'destkey' endpoint + * 'destkey' is 516-byte public key + * returns <0 on error, 0 on ok + * you still have to call sam3CloseSession() on failure + * sets ses->error on error + * don't send datagrams bigger than 31KB! + */ +extern int sam3DatagramSend(Sam3Session *ses, const char *destkey, + const void *buf, size_t bufsize); + +/* + * receives datagram and sets 'destkey' to source pubkey (if not RAW) + * returns <0 on error (buffer too small is error too) or number of bytes + * written to 'buf' you still have to call sam3CloseSession() on failure sets + * ses->error on error will necer receive datagrams bigger than 31KB (32KB for + * RAW) + */ +extern ssize_t sam3DatagramReceive(Sam3Session *ses, void *buf, size_t bufsize); + +/* + * generate random sam channel name + * return the size of the string + */ +extern size_t sam3GenChannelName(char *dest, size_t minlen, size_t maxlen); + +//////////////////////////////////////////////////////////////////////////////// +// NOT including '\0' terminator +static inline size_t sam3Base32EncodedLength(size_t size) { + return (((size + 5 - 1) / 5) * 8); +} + +// output 8 bytes for every 5 input +// return size or <0 on error +extern ssize_t sam3Base32Encode(char *dest, size_t destsz, const void *srcbuf, + size_t srcsize); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/supportlibs/libsam3/src/libsam3a/libsam3a.c b/supportlibs/libsam3/src/libsam3a/libsam3a.c new file mode 100644 index 000000000..40921e6ba --- /dev/null +++ b/supportlibs/libsam3/src/libsam3a/libsam3a.c @@ -0,0 +1,1757 @@ +/* async SAMv3 library + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include "libsam3a.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +int libsam3a_debug = 0; + +#define DEFAULT_TCP_PORT (7656) +#define DEFAULT_UDP_PORT (7655) + +//////////////////////////////////////////////////////////////////////////////// +uint64_t sam3atimeval2ms(const struct timeval *tv) { + return ((uint64_t)tv->tv_sec) * 1000 + ((uint64_t)tv->tv_usec) / 1000; +} + +void sam3ams2timeval(struct timeval *tv, uint64_t ms) { + tv->tv_sec = ms / 1000; + tv->tv_usec = (ms % 1000) * 1000; +} + +//////////////////////////////////////////////////////////////////////////////// +static inline int isValidKeyChar(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || + (ch >= '0' && ch <= '9') || ch == '-' || ch == '~'; +} + +int sam3aIsValidPubKey(const char *key) { + if (key != NULL && strlen(key) == SAM3A_PUBKEY_SIZE) { + for (int f = 0; f < SAM3A_PUBKEY_SIZE; ++f) + if (!isValidKeyChar(key[f])) + return 0; + return 1; + } + return 0; +} + +int sam3aIsValidPrivKey(const char *key) { + if (key != NULL && strlen(key) == SAM3A_PRIVKEY_SIZE) { + for (int f = 0; f < SAM3A_PRIVKEY_SIZE; ++f) + if (!isValidKeyChar(key[f])) + return 0; + return 1; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/* +static int sam3aSocketSetTimeoutSend (int fd, int timeoutms) { + if (fd >= 0 && timeoutms >= 0) { + struct timeval tv; + // + ms2timeval(&tv, timeoutms); + return (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 ? -1 : +0); + } + return -1; +} + + +static int sam3aSocketSetTimeoutReceive (int fd, int timeoutms) { + if (fd >= 0 && timeoutms >= 0) { + struct timeval tv; + // + ms2timeval(&tv, timeoutms); + return (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ? -1 : +0); + } + return -1; +} +*/ + +static int sam3aBytesAvail(int fd) { + int av = 0; + // + if (ioctl(fd, FIONREAD, &av) < 0) + return -1; + return av; +} + +static uint32_t sam3aResolveHost(const char *hostname) { + struct hostent *host; + // + if (hostname == NULL || !hostname[0]) + return 0; + if ((host = gethostbyname(hostname)) == NULL || host->h_name == NULL || + !host->h_addr_list[0][0]) { + if (libsam3a_debug) + fprintf(stderr, "ERROR: can't resolve '%s'\n", hostname); + return 0; + } + return ((struct in_addr *)host->h_addr_list[0])->s_addr; +} + +static int sam3aConnect(uint32_t ip, int port, int *complete) { + int fd, val = 1; + // + if (complete != NULL) + *complete = 0; + if (ip == 0 || ip == 0xffffffffUL || port < 1 || port > 65535) + return -1; + // + // yes, this is Linux-specific; you know what? i don't care. + if ((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) + return -1; + // + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + // + for (;;) { + struct sockaddr_in addr; + // + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + // + if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { + if (errno == EINPROGRESS) + break; // the process is started + if (errno != EINTR) { + close(fd); + return -1; + } + } else { + // connection complete + if (complete != NULL) + *complete = 1; + break; + } + } + // + return fd; +} + +// <0: error; 0: ok +static int sam3aDisconnect(int fd) { + if (fd >= 0) { + shutdown(fd, SHUT_RDWR); + return close(fd); + } + // + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +// <0: error; >=0: bytes sent +static int sam3aSendBytes(int fd, const void *buf, int bufSize) { + const char *c = (const char *)buf; + int total = 0; + // + if (fd < 0 || (buf == NULL && bufSize > 0)) + return -1; + // + while (bufSize > 0) { + int wr = send(fd, c, bufSize, MSG_NOSIGNAL); + // + if (wr < 0) { + if (errno == EINTR) + continue; // interrupted by signal + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // bufSize is too big + if (bufSize == 1) + break; // can't send anything + // try to send a half of a buffer + if ((wr = sam3aSendBytes(fd, c, bufSize / 2)) < 0) + return wr; // error + } else { + return -1; // alas + } + } + // + if (wr == 0) + break; // can't send anything + c += wr; + bufSize -= wr; + total += wr; + } + // + return total; +} + +/* <0: error; >=0: bytes received */ +/* note that you should call this function when there is some bytes to read, so + * 0 means 'connection closed' */ +/* +static int sam3aReceive (int fd, void *buf, int bufSize) { + char *c = (char *)buf; + int total = 0; + // + if (fd < 0 || (buf == NULL && bufSize > 0)) return -1; + // + while (bufSize > 0) { + int av = sam3aBytesAvail(fd), rd; + // + if (av == 0) break; // no more + if (av > bufSize) av = bufSize; + rd = recv(fd, c, av, 0); + if (rd < 0) { + if (errno == EINTR) continue; // interrupted by signal + if (errno == EAGAIN || errno == EWOULDBLOCK) break; // the thing that +should not be return -1; // error + } + if (rd == 0) break; + c += rd; + bufSize -= rd; + total += rd; + } + // + return total; +} +*/ + +//////////////////////////////////////////////////////////////////////////////// +char *sam3PrintfVA(int *plen, const char *fmt, va_list app) { + char buf[1024], *p = buf; + int size = sizeof(buf) - 1, len = 0; + // + if (plen != NULL) + *plen = 0; + for (;;) { + va_list ap; + char *np; + int n; + // + va_copy(ap, app); + n = vsnprintf(p, size, fmt, ap); + va_end(ap); + // + if (n > -1 && n < size) { + len = n; + break; + } + if (n > -1) + size = n + 1; + else + size *= 2; + if (p == buf) { + if ((p = malloc(size)) == NULL) + return NULL; + } else { + if ((np = realloc(p, size)) == NULL) { + free(p); + return NULL; + } + p = np; + } + } + // + if (p == buf) { + if ((p = malloc(len + 1)) == NULL) + return NULL; + memcpy(p, buf, len + 1); + } + if (plen != NULL) + *plen = len; + return p; +} + +__attribute__((format(printf, 2, 3))) char *sam3Printf(int *plen, + const char *fmt, ...) { + va_list ap; + char *res; + // + va_start(ap, fmt); + res = sam3PrintfVA(plen, fmt, ap); + va_end(ap); + // + return res; +} + +/* + * check if we have EOL in received socket data + * this function should be called when sam3aBytesAvail() result > 0 + * return: <0: error; 0: no EOL in bytes2check, else: # of bytes to EOL + * (including EOL itself) + */ +/* +static int sam3aCheckEOL (int fd, int bytes2check) { + char *d = dest; + // + if (bytes2check < 0 || fd < 0) return -1; + memset(dest, 0, maxSize); + while (maxSize > 1) { + char *e; + int rd = recv(fd, d, maxSize-1, MSG_PEEK); + // + if (rd < 0 && errno == EINTR) continue; // interrupted by signal + if (rd == 0) { + rd = recv(fd, d, 1, 0); + if (rd < 0 && errno == EINTR) continue; // interrupted by signal + if (d[0] == '\n') { + d[0] = 0; // remove '\n' + return 0; + } + } else { + if (rd < 0) return -1; // error or connection closed; alas + } + // check for EOL + d[maxSize-1] = 0; + if ((e = strchr(d, '\n')) != NULL) { + rd = e-d+1; // bytes to receive + if (sam3atcpReceive(fd, d, rd) < 0) return -1; // alas + d[rd-1] = 0; // remove '\n' + return 0; // done + } else { + // let's receive this part and go on + if (sam3atcpReceive(fd, d, rd) < 0) return -1; // alas + maxSize -= rd; + d += rd; + } + } + // alas, the string is too big + return -1; +} +*/ + +//////////////////////////////////////////////////////////////////////////////// +/* +int sam3audpSendToIP (uint32_t ip, int port, const void *buf, int bufSize) { + struct sockaddr_in addr; + int fd, res; + // + if (buf == NULL || bufSize < 1) return -1; + if (port < 1 || port > 65535) port = 7655; + // + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + if (libsam3a_debug) fprintf(stderr, "ERROR: can't create socket\n"); + return -1; + } + // + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + // + res = sendto(fd, buf, bufSize, 0, (struct sockaddr *)&addr, sizeof(addr)); + // + if (res < 0) { + if (libsam3a_debug) { + res = errno; + fprintf(stderr, "UDP ERROR (%d): %s\n", res, strerror(res)); + } + res = -1; + } else { + if (libsam3a_debug) fprintf(stderr, "UDP: %d bytes sent\n", res); + } + // + close(fd); + // + return (res >= 0 ? 0 : -1); +} + + +int sam3audpSendTo (const char *hostname, int port, const void *buf, int +bufSize, uint32_t *ip) { struct hostent *host = NULL; + // + if (buf == NULL || bufSize < 1) return -1; + if (hostname == NULL || !hostname[0]) hostname = "localhost"; + if (port < 1 || port > 65535) port = 7655; + // + host = gethostbyname(hostname); + if (host == NULL || host->h_name == NULL || !host->h_name[0]) { + if (libsam3a_debug) fprintf(stderr, "ERROR: can't resolve '%s'\n", +hostname); return -1; + } + // + if (ip != NULL) *ip = ((struct in_addr *)host->h_addr)->s_addr; + return sam3audpSendToIP(((struct in_addr *)host->h_addr)->s_addr, port, buf, +bufSize); +} +*/ + +//////////////////////////////////////////////////////////////////////////////// +typedef struct SAMFieldList { + char *name; + char *value; + struct SAMFieldList *next; +} SAMFieldList; + +static void sam3aFreeFieldList(SAMFieldList *list) { + while (list != NULL) { + SAMFieldList *c = list; + // + list = list->next; + if (c->name != NULL) + free(c->name); + if (c->value != NULL) + free(c->value); + free(c); + } +} + +static void sam3aDumpFieldList(const SAMFieldList *list) { + for (; list != NULL; list = list->next) { + fprintf(stderr, "%s=[%s]\n", list->name, list->value); + } +} + +static const char *sam3aFindField(const SAMFieldList *list, const char *field) { + if (list != NULL && field != NULL) { + for (list = list->next; list != NULL; list = list->next) { + if (list->name != NULL && strcmp(field, list->name) == 0) + return list->value; + } + } + return NULL; +} + +static char *xstrdup(const char *s, int len) { + if (len >= 0) { + char *res = malloc(len + 1); + // + if (res != NULL) { + if (len > 0) + memcpy(res, s, len); + res[len] = 0; + } + // + return res; + } + // + return NULL; +} + +// returns NULL if there are no more tokens +static inline const char *xstrtokend(const char *s) { + while (*s && isspace(*s)) + ++s; + // + if (*s) { + char qch = 0; + // + while (*s) { + if (*s == qch) { + qch = 0; + } else if (*s == '"') { + qch = *s; + } else if (qch) { + if (*s == '\\' && s[1]) + ++s; + } else if (isspace(*s)) { + break; + } + ++s; + } + } else { + s = NULL; + } + // + return s; +} + +SAMFieldList *sam3aParseReply(const char *rep) { + SAMFieldList *first = NULL, *last, *c; + const char *p = rep, *e, *e1; + // + // first 2 words + while (*p && isspace(*p)) + ++p; + if ((e = xstrtokend(p)) == NULL) + return NULL; + if ((e1 = xstrtokend(e)) == NULL) + return NULL; + // + if ((first = last = c = malloc(sizeof(SAMFieldList))) == NULL) + return NULL; + c->next = NULL; + c->name = c->value = NULL; + if ((c->name = xstrdup(p, e - p)) == NULL) + goto error; + while (*e && isspace(*e)) + ++e; + if ((c->value = xstrdup(e, e1 - e)) == NULL) + goto error; + // + p = e1; + while (*p) { + while (*p && isspace(*p)) + ++p; + if ((e = xstrtokend(p)) == NULL) + break; // no more tokens + // + if (libsam3a_debug) + fprintf(stderr, "<%s>\n", p); + // + if ((c = malloc(sizeof(SAMFieldList))) == NULL) + return NULL; + c->next = NULL; + c->name = c->value = NULL; + last->next = c; + last = c; + // + if ((e1 = memchr(p, '=', e - p)) != NULL) { + // key=value + if ((c->name = xstrdup(p, e1 - p)) == NULL) + goto error; + if ((c->value = xstrdup(e1 + 1, e - e1 - 1)) == NULL) + goto error; + } else { + // only key (there is no such replies in SAMv3, but... + if ((c->name = xstrdup(p, e - p)) == NULL) + goto error; + if ((c->value = strdup("")) == NULL) + goto error; + } + p = e; + } + // + if (libsam3a_debug) + sam3aDumpFieldList(first); + // + return first; +error: + sam3aFreeFieldList(first); + return NULL; +} + +// example: +// r0: 'HELLO' +// r1: 'REPLY' +// field: NULL or 'RESULT' +// VALUE: NULL or 'OK' +// returns bool +int sam3aIsGoodReply(const SAMFieldList *list, const char *r0, const char *r1, + const char *field, const char *value) { + if (list != NULL && list->name != NULL && list->value != NULL) { + if (r0 != NULL && strcmp(r0, list->name) != 0) + return 0; + if (r1 != NULL && strcmp(r1, list->value) != 0) + return 0; + if (field != NULL) { + for (list = list->next; list != NULL; list = list->next) { + if (list->name == NULL || list->value == NULL) + return 0; // invalid list, heh + if (strcmp(field, list->name) == 0) { + if (value != NULL && strcmp(value, list->value) != 0) + return 0; + return 1; + } + } + } + return 1; + } + return 0; +} + +// NULL: error; else: list of fields +// first item is always 2-word reply, with first word in name and second in +// value +/* +SAMFieldList *sam3aReadReply (int fd) { + char rep[2048]; // should be enough for any reply + // + if (sam3atcpReceiveStr(fd, rep, sizeof(rep)) < 0) return NULL; + if (libsam3a_debug) fprintf(stderr, "SAM REPLY: [%s]\n", rep); + return sam3aParseReply(rep); +} +*/ + +//////////////////////////////////////////////////////////////////////////////// +// by Bob Jenkins +// public domain +// http://burtleburtle.net/bob/rand/smallprng.html +// +//////////////////////////////////////////////////////////////////////////////// +typedef struct { + uint32_t a, b, c, d; +} BJRandCtx; + +#define BJPRNG_ROT(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) + +static uint32_t bjprngRand(BJRandCtx *x) { + uint32_t e; + /* original: + e = x->a-BJPRNG_ROT(x->b, 27); + x->a = x->b^BJPRNG_ROT(x->c, 17); + x->b = x->c+x->d; + x->c = x->d+e; + x->d = e+x->a; + */ + /* better, but slower at least in idiotic m$vc */ + e = x->a - BJPRNG_ROT(x->b, 23); + x->a = x->b ^ BJPRNG_ROT(x->c, 16); + x->b = x->c + BJPRNG_ROT(x->d, 11); + x->c = x->d + e; + x->d = e + x->a; + // + return x->d; +} + +static void bjprngInit(BJRandCtx *x, uint32_t seed) { + x->a = 0xf1ea5eed; + x->b = x->c = x->d = seed; + for (int i = 0; i < 20; ++i) + bjprngRand(x); +} + +static inline uint32_t hashint(uint32_t a) { + a -= (a << 6); + a ^= (a >> 17); + a -= (a << 9); + a ^= (a << 4); + a -= (a << 3); + a ^= (a << 10); + a ^= (a >> 15); + return a; +} + +static uint32_t genSeed(void) { + volatile uint32_t seed = 1; + uint32_t res; +#ifndef WIN32 + struct sysinfo sy; + pid_t pid = getpid(); + // + sysinfo(&sy); + res = hashint((uint32_t)pid) ^ hashint((uint32_t)time(NULL)) ^ + hashint((uint32_t)sy.sharedram) ^ hashint((uint32_t)sy.bufferram) ^ + hashint((uint32_t)sy.uptime); +#else + res = hashint((uint32_t)GetCurrentProcessId()) ^ + hashint((uint32_t)GetTickCount()); +#endif + res += __sync_fetch_and_add(&seed, 1); + // + return hashint(res); +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aGenChannelName(char *dest, int minlen, int maxlen) { + BJRandCtx rc; + int len; + // + if (dest == NULL || minlen < 1 || maxlen < minlen || minlen > 65536 || + maxlen > 65536) + return -1; + bjprngInit(&rc, genSeed()); + len = minlen + (bjprngRand(&rc) % (maxlen - minlen + 1)); + while (len-- > 0) { + int ch = bjprngRand(&rc) % 64; + // + if (ch >= 0 && ch < 10) + ch += '0'; + else if (ch >= 10 && ch < 36) + ch += 'A' - 10; + else if (ch >= 36 && ch < 62) + ch += 'a' - 36; + else if (ch == 62) + ch = '-'; + else if (ch == 63) + ch = '_'; + else if (ch > 64) + abort(); + *dest++ = ch; + } + *dest++ = 0; + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aIsActiveSession(const Sam3ASession *ses) { + return (ses != NULL && ses->fd >= 0 && !ses->cancelled); +} + +int sam3aIsActiveConnection(const Sam3AConnection *conn) { + return (conn != NULL && conn->fd >= 0 && !conn->cancelled); +} + +//////////////////////////////////////////////////////////////////////////////// +static inline void strcpyerrs(Sam3ASession *ses, const char *errstr) { + // memset(ses->error, 0, sizeof(ses->error)); + ses->error[sizeof(ses->error) - 1] = 0; + if (errstr != NULL) + strncpy(ses->error, errstr, sizeof(ses->error) - 1); +} + +static inline void strcpyerrc(Sam3AConnection *conn, const char *errstr) { + // memset(conn->error, 0, sizeof(conn->error)); + conn->error[sizeof(conn->error) - 1] = 0; + if (errstr != NULL) + strncpy(conn->error, errstr, sizeof(conn->error) - 1); +} + +static void connDisconnect(Sam3AConnection *conn) { + conn->cbAIOProcessorR = conn->cbAIOProcessorW = NULL; + if (conn->aio.data != NULL) { + free(conn->aio.data); + conn->aio.data = NULL; + } + if (!conn->cancelled && conn->fd >= 0) { + conn->cancelled = 1; + shutdown(conn->fd, SHUT_RDWR); + if (conn->callDisconnectCB && conn->cb.cbDisconnected != NULL) + conn->cb.cbDisconnected(conn); + } +} + +static void sesDisconnect(Sam3ASession *ses) { + ses->cbAIOProcessorR = ses->cbAIOProcessorW = NULL; + if (ses->aio.data != NULL) { + free(ses->aio.data); + ses->aio.data = NULL; + } + if (!ses->cancelled && ses->fd >= 0) { + ses->cancelled = 1; + shutdown(ses->fd, SHUT_RDWR); + for (Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) + connDisconnect(c); + if (ses->callDisconnectCB && ses->cb.cbDisconnected != NULL) + ses->cb.cbDisconnected(ses); + } +} + +static void sesError(Sam3ASession *ses, const char *errstr) { + if (errstr == NULL || !errstr[0]) + errstr = "I2P_ERROR"; + strcpyerrs(ses, errstr); + if (ses->cb.cbError != NULL) + ses->cb.cbError(ses); + sesDisconnect(ses); +} + +static void connError(Sam3AConnection *conn, const char *errstr) { + if (errstr == NULL || !errstr[0]) + errstr = "I2P_ERROR"; + strcpyerrc(conn, errstr); + if (conn->cb.cbError != NULL) + conn->cb.cbError(conn); + connDisconnect(conn); +} + +//////////////////////////////////////////////////////////////////////////////// +static int aioSender(int fd, Sam3AIO *aio) { + int wr = sam3aSendBytes(fd, aio->data + aio->dataPos, + aio->dataUsed - aio->dataPos); + // + if (wr < 0) + return -1; + aio->dataPos += wr; + return 0; +} + +// dataUsed: max line size (with '\n') +// dataSize: must be at least (dataUsed+1) +static int aioLineReader(int fd, Sam3AIO *aio) { + // + for (;;) { + int av = sam3aBytesAvail(fd), rd; + // + if (av < 0) + return -1; + if (av == 0) + return 0; // do nothing + if (aio->dataPos >= aio->dataUsed - 1) + return -1; // line too long + if ((rd = (aio->dataUsed - 1) - aio->dataPos) > av) + rd = av; + if ((rd = recv(fd, aio->data + aio->dataPos, rd, MSG_PEEK)) < 0) { + if (errno == EINTR) + continue; + return -1; + } + if (rd == 0) + return 0; // do nothing + // now look for '\n' + for (int f = aio->dataPos; f < aio->dataPos + rd; ++f) { + if (aio->data[f] == '\n') { + // got it! + if (recv(fd, aio->data + aio->dataPos, f - aio->dataPos + 1, 0) != + f + 1) + return -1; // the thing that should not be + aio->data[f] = 0; // convert to asciiz + aio->dataUsed = aio->dataPos = f; // length + return 1; // '\n' found! + } + if (!aio->data[f]) + return -1; // there should not be zero bytes + } + // no '\n' found + if (recv(fd, aio->data + aio->dataPos, rd, 0) != rd) + return -1; // the thing that should not be + aio->dataPos += rd; + } +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioSesCmdReplyReader(Sam3ASession *ses) { + int res = aioLineReader(ses->fd, &ses->aio); + // + if (res < 0) { + sesError(ses, "IO_ERROR"); + return; + } + if (res > 0) { + // we got full line + if (libsam3a_debug) + fprintf(stderr, "CMDREPLY: %s\n", ses->aio.data); + if (ses->aio.cbReplyCheckSes != NULL) + ses->aio.cbReplyCheckSes(ses); + } +} + +static void aioSesCmdSender(Sam3ASession *ses) { + if (ses->aio.dataPos < ses->aio.dataUsed) { + if (aioSender(ses->fd, &ses->aio) < 0) { + sesError(ses, "IO_ERROR"); + return; + } + } + // + if (ses->aio.dataPos == ses->aio.dataUsed) { + // hello sent, now wait for reply + // 2048 bytes of reply line should be enough + if (ses->aio.dataSize < 2049) { + char *n = realloc(ses->aio.data, 2049); + // + if (n == NULL) { + sesError(ses, "MEMORY_ERROR"); + return; + } + ses->aio.data = n; + ses->aio.dataSize = 2049; + } + ses->aio.dataUsed = 2048; + ses->aio.dataPos = 0; + ses->cbAIOProcessorR = aioSesCmdReplyReader; + ses->cbAIOProcessorW = NULL; + } +} + +static __attribute__((format(printf, 3, 4))) int +aioSesSendCmdWaitReply(Sam3ASession *ses, void (*cbCheck)(Sam3ASession *ses), + const char *fmt, ...) { + va_list ap; + char *str; + int len; + // + va_start(ap, fmt); + str = sam3PrintfVA(&len, fmt, ap); + va_end(ap); + // + if (str == NULL) + return -1; + if (ses->aio.data != NULL) + free(ses->aio.data); + ses->aio.data = str; + ses->aio.dataUsed = len; + ses->aio.dataSize = len + 1; + ses->aio.dataPos = 0; + ses->aio.cbReplyCheckSes = cbCheck; + ses->cbAIOProcessorR = NULL; + ses->cbAIOProcessorW = aioSesCmdSender; + // + if (libsam3a_debug) + fprintf(stderr, "CMD: %s", str); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioSesHelloChecker(Sam3ASession *ses) { + SAMFieldList *rep = sam3aParseReply(ses->aio.data); + // + if (rep != NULL && sam3aIsGoodReply(rep, "HELLO", "REPLY", "RESULT", "OK") && + sam3aIsGoodReply(rep, NULL, NULL, "VERSION", "3.0")) { + ses->cbAIOProcessorR = ses->cbAIOProcessorW = NULL; + sam3aFreeFieldList(rep); + if (ses->aio.udata != NULL) { + void (*cbComplete)(Sam3ASession * ses) = ses->aio.udata; + // + cbComplete(ses); + } + } else { + sam3aFreeFieldList(rep); + sesError(ses, NULL); + } +} + +static int sam3aSesStartHandshake(Sam3ASession *ses, + void (*cbComplete)(Sam3ASession *ses)) { + if (cbComplete != NULL) + ses->aio.udata = cbComplete; + if (aioSesSendCmdWaitReply(ses, aioSesHelloChecker, "%s\n", + "HELLO VERSION MIN=3.0 MAX=3.0") < 0) + return -1; + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioSesNameMeChecker(Sam3ASession *ses) { + SAMFieldList *rep = sam3aParseReply(ses->aio.data); + const char *v = NULL; + // + if (rep == NULL) { + sesError(ses, NULL); + return; + } + if (!sam3aIsGoodReply(rep, "NAMING", "REPLY", "RESULT", "OK") || + (v = sam3aFindField(rep, "VALUE")) == NULL || + strlen(v) != SAM3A_PUBKEY_SIZE) { + // if (libsam3a_debug) fprintf(stderr, "sam3aCreateSession: invalid NAMING + // reply (%d)...\n", (v != NULL ? strlen(v) : -1)); + if ((v = sam3aFindField(rep, "RESULT")) != NULL && strcmp(v, "OK") == 0) + v = NULL; + sesError(ses, v); + sam3aFreeFieldList(rep); + return; + } + strcpy(ses->pubkey, v); + sam3aFreeFieldList(rep); + // + ses->cbAIOProcessorR = ses->cbAIOProcessorW = NULL; + ses->callDisconnectCB = 1; + if (ses->cb.cbCreated != NULL) + ses->cb.cbCreated(ses); +} + +static void aioSesCreateChecker(Sam3ASession *ses) { + SAMFieldList *rep = sam3aParseReply(ses->aio.data); + const char *v; + // + if (rep == NULL) { + sesError(ses, NULL); + return; + } + if (!sam3aIsGoodReply(rep, "SESSION", "STATUS", "RESULT", "OK") || + (v = sam3aFindField(rep, "DESTINATION")) == NULL || + strlen(v) != SAM3A_PRIVKEY_SIZE) { + sam3aFreeFieldList(rep); + if ((v = sam3aFindField(rep, "RESULT")) != NULL && strcmp(v, "OK") == 0) + v = NULL; + sesError(ses, v); + return; + } + // ok + // fprintf(stderr, "\nPK: %s\n", v); + strcpy(ses->privkey, v); + sam3aFreeFieldList(rep); + // get our public key + if (aioSesSendCmdWaitReply(ses, aioSesNameMeChecker, "%s\n", + "NAMING LOOKUP NAME=ME") < 0) { + sesError(ses, "MEMORY_ERROR"); + } +} + +// handshake for SESSION CREATE complete +static void aioSesHandshacked(Sam3ASession *ses) { + static const char *typenames[3] = {"RAW", "DATAGRAM", "STREAM"}; + // + if (aioSesSendCmdWaitReply( + ses, aioSesCreateChecker, + "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s%s%s\n", + typenames[(int)ses->type], ses->channel, ses->privkey, + (ses->params != NULL ? " " : ""), + (ses->params != NULL ? ses->params : "")) < 0) { + sesError(ses, "MEMORY_ERROR"); + } +} + +static void aioSesConnected(Sam3ASession *ses) { + int res; + socklen_t len = sizeof(res); + // + if (getsockopt(ses->fd, SOL_SOCKET, SO_ERROR, &res, &len) == 0 && res == 0) { + // ok, connected + if (sam3aSesStartHandshake(ses, NULL) < 0) + sesError(ses, NULL); + } else { + // connection error + sesError(ses, "CONNECTION_ERROR"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aCreateSessionEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, + const char *hostname, int port, const char *privkey, + Sam3ASessionType type, const char *params, + int timeoutms) { + if (ses != NULL) { + // int complete = 0; + // + memset(ses, 0, sizeof(Sam3ASession)); + ses->fd = -1; + if (cb != NULL) + ses->cb = *cb; + if (hostname == NULL || !hostname[0]) + hostname = "127.0.0.1"; + if (port < 0 || port > 65535) + goto error; + if (privkey != NULL && strlen(privkey) != SAM3A_PRIVKEY_SIZE) + goto error; + if ((int)type < 0 || (int)type > 2) + goto error; + if (privkey == NULL) + privkey = "TRANSIENT"; + strcpy(ses->privkey, privkey); + if (params != NULL && (ses->params = strdup(params)) == NULL) + goto error; + ses->timeoutms = timeoutms; + // + if (!port) + port = DEFAULT_TCP_PORT; + ses->type = type; + ses->port = (type == SAM3A_SESSION_STREAM ? port : DEFAULT_UDP_PORT); + if ((ses->ip = sam3aResolveHost(hostname)) == 0) + goto error; + sam3aGenChannelName(ses->channel, 32, 64); + if (libsam3a_debug) + fprintf(stderr, "sam3aCreateSession: channel=[%s]\n", ses->channel); + // + ses->aio.udata = aioSesHandshacked; + ses->cbAIOProcessorW = aioSesConnected; + if ((ses->fd = sam3aConnect(ses->ip, port, NULL)) < 0) + goto error; + /* + if (complete) { + ses->cbAIOProcessorW(ses); + if (!sam3aIsActiveSession(ses)) return -1; + } + */ + // + return 0; // ok, connection process initiated + error: + if (ses->fd >= 0) + sam3aDisconnect(ses->fd); + if (ses->params != NULL) + free(ses->params); + memset(ses, 0, sizeof(Sam3ASession)); + ses->fd = -1; + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aCancelSession(Sam3ASession *ses) { + if (ses != NULL) { + sesDisconnect(ses); + return 0; + } + return -1; +} + +int sam3aCloseSession(Sam3ASession *ses) { + if (ses != NULL) { + sam3aCancelSession(ses); + while (ses->connlist != NULL) + sam3aCloseConnection(ses->connlist); + if (ses->cb.cbDestroy != NULL) + ses->cb.cbDestroy(ses); + if (ses->params != NULL) { + free(ses->params); + ses->params = NULL; + } + memset(ses, 0, sizeof(Sam3ASession)); + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioSesKeyGenChecker(Sam3ASession *ses) { + SAMFieldList *rep = sam3aParseReply(ses->aio.data); + // + if (rep == NULL) { + sesError(ses, NULL); + return; + } + if (sam3aIsGoodReply(rep, "DEST", "REPLY", NULL, NULL)) { + const char *pub = sam3aFindField(rep, "PUB"), + *priv = sam3aFindField(rep, "PRIV"); + // + if (pub != NULL && strlen(pub) == SAM3A_PUBKEY_SIZE && priv != NULL && + strlen(priv) == SAM3A_PRIVKEY_SIZE) { + strcpy(ses->pubkey, pub); + strcpy(ses->privkey, priv); + sam3aFreeFieldList(rep); + if (ses->cb.cbCreated != NULL) + ses->cb.cbCreated(ses); + sam3aCancelSession(ses); + return; + } + } + sam3aFreeFieldList(rep); + sesError(ses, NULL); +} + +// handshake for SESSION CREATE complete +static void aioSesKeyGenHandshacked(Sam3ASession *ses) { + if (aioSesSendCmdWaitReply(ses, aioSesKeyGenChecker, "%s\n", + "DEST GENERATE") < 0) { + sesError(ses, "MEMORY_ERROR"); + } +} + +int sam3aGenerateKeysEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, + const char *hostname, int port, int timeoutms) { + if (ses != NULL) { + memset(ses, 0, sizeof(Sam3ASession)); + ses->fd = -1; + if (cb != NULL) + ses->cb = *cb; + if (hostname == NULL || !hostname[0]) + hostname = "127.0.0.1"; + if (port < 0 || port > 65535) + goto error; + ses->timeoutms = timeoutms; + // + if (!port) + port = DEFAULT_TCP_PORT; + ses->port = port; + if ((ses->ip = sam3aResolveHost(hostname)) == 0) + goto error; + // + ses->aio.udata = aioSesKeyGenHandshacked; + ses->cbAIOProcessorW = aioSesConnected; + if ((ses->fd = sam3aConnect(ses->ip, port, NULL)) < 0) + goto error; + // + return 0; // ok, connection process initiated + error: + if (ses->fd >= 0) + sam3aDisconnect(ses->fd); + if (ses->params != NULL) + free(ses->params); + memset(ses, 0, sizeof(Sam3ASession)); + ses->fd = -1; + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioSesNameResChecker(Sam3ASession *ses) { + SAMFieldList *rep = sam3aParseReply(ses->aio.data); + // + if (rep == NULL) { + sesError(ses, NULL); + return; + } + if (sam3aIsGoodReply(rep, "NAMING", "REPLY", "RESULT", NULL)) { + const char *rs = sam3aFindField(rep, "RESULT"), + *pub = sam3aFindField(rep, "VALUE"); + // + if (strcmp(rs, "OK") == 0) { + if (pub != NULL && strlen(pub) == SAM3A_PUBKEY_SIZE) { + strcpy(ses->destkey, pub); + sam3aFreeFieldList(rep); + if (ses->cb.cbCreated != NULL) + ses->cb.cbCreated(ses); + sam3aCancelSession(ses); + return; + } + sam3aFreeFieldList(rep); + sesError(ses, NULL); + } else { + sesError(ses, rs); + sam3aFreeFieldList(rep); + } + } +} + +// handshake for SESSION CREATE complete +static void aioSesNameResHandshacked(Sam3ASession *ses) { + if (aioSesSendCmdWaitReply(ses, aioSesNameResChecker, + "NAMING LOOKUP NAME=%s\n", ses->params) < 0) { + sesError(ses, "MEMORY_ERROR"); + } +} + +int sam3aNameLookupEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, + const char *hostname, int port, const char *name, + int timeoutms) { + if (ses != NULL) { + memset(ses, 0, sizeof(Sam3ASession)); + ses->fd = -1; + if (cb != NULL) + ses->cb = *cb; + if (name == NULL || !name[0] || + (name[0] && toupper(name[0]) == 'M' && name[1] && + toupper(name[1]) == 'E' && (!name[2] || isspace(name[2])))) + goto error; + if (hostname == NULL || !hostname[0]) + hostname = "127.0.0.1"; + if (port < 0 || port > 65535) + goto error; + if ((ses->params = strdup(name)) == NULL) + goto error; + ses->timeoutms = timeoutms; + // + if (!port) + port = DEFAULT_TCP_PORT; + ses->port = port; + if ((ses->ip = sam3aResolveHost(hostname)) == 0) + goto error; + // + ses->aio.udata = aioSesNameResHandshacked; + ses->cbAIOProcessorW = aioSesConnected; + if ((ses->fd = sam3aConnect(ses->ip, port, NULL)) < 0) + goto error; + // + return 0; // ok, connection process initiated + error: + if (ses->fd >= 0) + sam3aDisconnect(ses->fd); + if (ses->params != NULL) + free(ses->params); + memset(ses, 0, sizeof(Sam3ASession)); + ses->fd = -1; + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioConnCmdReplyReader(Sam3AConnection *conn) { + int res = aioLineReader(conn->fd, &conn->aio); + // + if (res < 0) { + connError(conn, "IO_ERROR"); + return; + } + if (res > 0) { + // we got full line + if (libsam3a_debug) + fprintf(stderr, "CMDREPLY: %s\n", conn->aio.data); + if (conn->aio.cbReplyCheckConn != NULL) + conn->aio.cbReplyCheckConn(conn); + } +} + +static void aioConnCmdSender(Sam3AConnection *conn) { + if (conn->aio.dataPos < conn->aio.dataUsed) { + if (aioSender(conn->fd, &conn->aio) < 0) { + connError(conn, "IO_ERROR"); + return; + } + } + // + if (conn->aio.dataPos == conn->aio.dataUsed) { + // hello sent, now wait for reply + // 2048 bytes of reply line should be enough + if (conn->aio.dataSize < 2049) { + char *n = realloc(conn->aio.data, 2049); + // + if (n == NULL) { + connError(conn, "MEMORY_ERROR"); + return; + } + conn->aio.data = n; + conn->aio.dataSize = 2049; + } + conn->aio.dataUsed = 2048; + conn->aio.dataPos = 0; + conn->cbAIOProcessorR = aioConnCmdReplyReader; + conn->cbAIOProcessorW = NULL; + } +} + +static __attribute__((format(printf, 3, 4))) int +aioConnSendCmdWaitReply(Sam3AConnection *conn, + void (*cbCheck)(Sam3AConnection *conn), const char *fmt, + ...) { + va_list ap; + char *str; + int len; + // + va_start(ap, fmt); + str = sam3PrintfVA(&len, fmt, ap); + va_end(ap); + // + if (str == NULL) + return -1; + if (conn->aio.data != NULL) + free(conn->aio.data); + conn->aio.data = str; + conn->aio.dataUsed = len; + conn->aio.dataSize = len + 1; + conn->aio.dataPos = 0; + conn->aio.cbReplyCheckConn = cbCheck; + conn->cbAIOProcessorR = NULL; + conn->cbAIOProcessorW = aioConnCmdSender; + // + if (libsam3a_debug) + fprintf(stderr, "CMD: %s", str); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioConnDataReader(Sam3AConnection *conn) { + char *buf = NULL; + int bufsz = 0; + // + while (sam3aIsActiveConnection(conn)) { + int av = sam3aBytesAvail(conn->fd), rd; + // + if (av < 0) { + if (buf != NULL) + free(buf); + connError(conn, "IO_ERROR"); + return; + } + if (av == 0) + av = 1; + if (bufsz < av) { + char *n = realloc(buf, av + 1); + // + if (n == NULL) { + if (buf != NULL) + free(buf); + connError(conn, "IO_ERROR"); + return; + } + buf = n; + bufsz = av; + } + memset(buf, 0, av + 1); + // + rd = recv(conn->fd, buf, av, 0); + // + if (rd < 0) { + if (errno == EINTR) + continue; // interrupted by signal + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; // no more data + free(buf); + connError(conn, "IO_ERROR"); + return; + } + // + if (rd == 0) { + // connection closed + free(buf); + connDisconnect(conn); + return; + } + // + if (conn->cb.cbRead != NULL) + conn->cb.cbRead(conn, buf, rd); + } + free(buf); +} + +static void aioConnDataWriter(Sam3AConnection *conn) { + if (!sam3aIsActiveConnection(conn)) { + conn->aio.dataPos = conn->aio.dataUsed = 0; + return; + } + // + if (conn->aio.dataPos >= conn->aio.dataUsed) { + conn->aio.dataPos = conn->aio.dataUsed = 0; + return; + } + // + while (sam3aIsActiveConnection(conn) && + conn->aio.dataPos < conn->aio.dataUsed) { + int wr = sam3aSendBytes(conn->fd, conn->aio.data + conn->aio.dataPos, + conn->aio.dataUsed - conn->aio.dataPos); + // + if (wr < 0) { + connError(conn, "IO_ERROR"); + return; + } + if (wr == 0) + break; // can't write more bytes + conn->aio.dataPos += wr; + if (conn->aio.dataPos < conn->aio.dataUsed) { + memmove(conn->aio.data, conn->aio.data + conn->aio.dataPos, + conn->aio.dataUsed - conn->aio.dataPos); + conn->aio.dataUsed -= conn->aio.dataPos; + conn->aio.dataPos = 0; + } + } + // + if (conn->aio.dataPos >= conn->aio.dataUsed) { + conn->aio.dataPos = conn->aio.dataUsed = 0; + if (conn->cb.cbSent != NULL) + conn->cb.cbSent(conn); + if (conn->aio.dataSize > 8192) { + // shrink buffer + char *nn = realloc(conn->aio.data, 8192); + // + if (nn != NULL) { + conn->aio.data = nn; + conn->aio.dataSize = 8192; + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioConnHelloChecker(Sam3AConnection *conn) { + SAMFieldList *rep = sam3aParseReply(conn->aio.data); + // + if (rep != NULL && sam3aIsGoodReply(rep, "HELLO", "REPLY", "RESULT", "OK") && + sam3aIsGoodReply(rep, NULL, NULL, "VERSION", "3.0")) { + conn->cbAIOProcessorR = conn->cbAIOProcessorW = NULL; + sam3aFreeFieldList(rep); + if (conn->aio.udata != NULL) { + void (*cbComplete)(Sam3AConnection * conn) = conn->aio.udata; + // + cbComplete(conn); + } + } else { + sam3aFreeFieldList(rep); + connError(conn, NULL); + } +} + +static int sam3aConnStartHandshake(Sam3AConnection *conn, + void (*cbComplete)(Sam3AConnection *conn)) { + if (cbComplete != NULL) + conn->aio.udata = cbComplete; + if (aioConnSendCmdWaitReply(conn, aioConnHelloChecker, "%s\n", + "HELLO VERSION MIN=3.0 MAX=3.0") < 0) + return -1; + return 0; +} + +static void aioConnConnected(Sam3AConnection *conn) { + int res; + socklen_t len = sizeof(res); + // + if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &res, &len) == 0 && res == 0) { + // ok, connected + if (sam3aConnStartHandshake(conn, NULL) < 0) + connError(conn, NULL); + } else { + // connection error + connError(conn, "CONNECTION_ERROR"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioConnConnectChecker(Sam3AConnection *conn) { + SAMFieldList *rep = sam3aParseReply(conn->aio.data); + // + if (rep == NULL) { + connError(conn, NULL); + return; + } + if (!sam3aIsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { + const char *v = sam3aFindField(rep, "RESULT"); + // + connError(conn, v); + sam3aFreeFieldList(rep); + } else { + // no error + sam3aFreeFieldList(rep); + conn->callDisconnectCB = 1; + conn->cbAIOProcessorR = aioConnDataReader; + conn->cbAIOProcessorW = aioConnDataWriter; + conn->aio.dataPos = conn->aio.dataUsed = 0; + if (conn->cb.cbConnected != NULL) + conn->cb.cbConnected(conn); + // indicate that we are ready for new data + if (sam3aIsActiveConnection(conn) && conn->cb.cbSent != NULL) + conn->cb.cbSent(conn); + } +} + +// handshake for SESSION CREATE complete +static void aioConConnectHandshacked(Sam3AConnection *conn) { + if (aioConnSendCmdWaitReply(conn, aioConnConnectChecker, + "STREAM CONNECT ID=%s DESTINATION=%s\n", + conn->ses->channel, conn->destkey) < 0) { + connError(conn, "MEMORY_ERROR"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +Sam3AConnection *sam3aStreamConnectEx(Sam3ASession *ses, + const Sam3AConnectionCallbacks *cb, + const char *destkey, int timeoutms) { + if (sam3aIsActiveSession(ses) && ses->type == SAM3A_SESSION_STREAM && + destkey != NULL && strlen(destkey) == SAM3A_PUBKEY_SIZE) { + Sam3AConnection *conn = calloc(1, sizeof(Sam3AConnection)); + // + if (conn == NULL) + return NULL; + if (cb != NULL) + conn->cb = *cb; + strcpy(conn->destkey, destkey); + conn->timeoutms = timeoutms; + // + conn->aio.udata = aioConConnectHandshacked; + conn->cbAIOProcessorW = aioConnConnected; + if ((conn->fd = sam3aConnect(ses->ip, ses->port, NULL)) < 0) + goto error; + // + conn->ses = ses; + conn->next = ses->connlist; + ses->connlist = conn; + return conn; // ok, connection process initiated + error: + if (conn->fd >= 0) + sam3aDisconnect(conn->fd); + memset(conn, 0, sizeof(Sam3AConnection)); + free(conn); + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +static void aioConnAcceptCheckerA(Sam3AConnection *conn) { + SAMFieldList *rep = sam3aParseReply(conn->aio.data); + // + if (rep != NULL || strlen(conn->aio.data) != SAM3A_PUBKEY_SIZE || + !sam3aIsValidPubKey(conn->aio.data)) { + sam3aFreeFieldList(rep); + connError(conn, NULL); + return; + } + sam3aFreeFieldList(rep); + strcpy(conn->destkey, conn->aio.data); + conn->callDisconnectCB = 1; + conn->cbAIOProcessorR = aioConnDataReader; + conn->cbAIOProcessorW = aioConnDataWriter; + conn->aio.dataPos = conn->aio.dataUsed = 0; + if (conn->cb.cbAccepted != NULL) + conn->cb.cbAccepted(conn); + // indicate that we are ready for new data + if (sam3aIsActiveConnection(conn) && conn->cb.cbSent != NULL) + conn->cb.cbSent(conn); +} + +static void aioConnAcceptChecker(Sam3AConnection *conn) { + SAMFieldList *rep = sam3aParseReply(conn->aio.data); + // + if (rep == NULL) { + connError(conn, NULL); + return; + } + if (!sam3aIsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { + const char *v = sam3aFindField(rep, "RESULT"); + // + connError(conn, v); + sam3aFreeFieldList(rep); + } else { + // no error + sam3aFreeFieldList(rep); + // 2048 bytes of reply line should be enough + if (conn->aio.dataSize < 2049) { + char *n = realloc(conn->aio.data, 2049); + // + if (n == NULL) { + connError(conn, "MEMORY_ERROR"); + return; + } + conn->aio.data = n; + conn->aio.dataSize = 2049; + } + conn->aio.dataUsed = 2048; + conn->aio.dataPos = 0; + conn->cbAIOProcessorR = aioConnCmdReplyReader; + conn->cbAIOProcessorW = NULL; + conn->aio.cbReplyCheckConn = aioConnAcceptCheckerA; + } +} + +// handshake for SESSION CREATE complete +static void aioConAcceptHandshacked(Sam3AConnection *conn) { + if (aioConnSendCmdWaitReply(conn, aioConnAcceptChecker, + "STREAM ACCEPT ID=%s\n", + conn->ses->channel) < 0) { + connError(conn, "MEMORY_ERROR"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +Sam3AConnection *sam3aStreamAcceptEx(Sam3ASession *ses, + const Sam3AConnectionCallbacks *cb, + int timeoutms) { + if (sam3aIsActiveSession(ses) && ses->type == SAM3A_SESSION_STREAM) { + Sam3AConnection *conn = calloc(1, sizeof(Sam3AConnection)); + // + if (conn == NULL) + return NULL; + if (cb != NULL) + conn->cb = *cb; + conn->timeoutms = timeoutms; + // + conn->aio.udata = aioConAcceptHandshacked; + conn->cbAIOProcessorW = aioConnConnected; + if ((conn->fd = sam3aConnect(ses->ip, ses->port, NULL)) < 0) + goto error; + // + conn->ses = ses; + conn->next = ses->connlist; + ses->connlist = conn; + return conn; // ok, connection process initiated + error: + if (conn->fd >= 0) + sam3aDisconnect(conn->fd); + memset(conn, 0, sizeof(Sam3AConnection)); + free(conn); + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aSend(Sam3AConnection *conn, const void *data, int datasize) { + if (datasize == -1) + datasize = (data != NULL ? strlen((const char *)data) : 0); + // + if (sam3aIsActiveConnection(conn) && conn->callDisconnectCB && + conn->cbAIOProcessorW != NULL && + ((datasize > 0 && data != NULL) || datasize == 0)) { + // try to add data to send buffer + if (datasize > 0) { + if (conn->aio.dataUsed + datasize > conn->aio.dataSize) { + // we need more pepper! + int newsz = conn->aio.dataUsed + datasize; + char *nb = realloc(conn->aio.data, newsz); + // + if (nb == NULL) + return -1; // alas + conn->aio.data = nb; + conn->aio.dataSize = newsz; + } + // + memcpy(conn->aio.data + conn->aio.dataUsed, data, datasize); + conn->aio.dataUsed += datasize; + } + return 0; + } + // + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aIsHaveActiveConnections(const Sam3ASession *ses) { + if (sam3aIsActiveSession(ses)) { + for (const Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) { + if (sam3aIsActiveConnection(c)) + return 1; + } + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aCancelConnection(Sam3AConnection *conn) { + if (conn != NULL) { + connDisconnect(conn); + return 0; + } + return -1; +} + +int sam3aCloseConnection(Sam3AConnection *conn) { + if (conn != NULL) { + sam3aCancelConnection(conn); + if (conn->cb.cbDestroy != NULL) + conn->cb.cbDestroy(conn); + for (Sam3AConnection *p = NULL, *c = conn->ses->connlist; c != NULL; + p = c, c = c->next) { + if (c == conn) { + // got it! + if (p == NULL) + c->ses->connlist = c->next; + else + p->next = c->next; + break; + } + } + if (conn->params != NULL) { + free(conn->params); + conn->params = NULL; + } + memset(conn, 0, sizeof(Sam3AConnection)); + free(conn); + } + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +int sam3aAddSessionToFDS(Sam3ASession *ses, int maxfd, fd_set *rds, + fd_set *wrs) { + if (ses != NULL) { + if (sam3aIsActiveSession(ses)) { + if (rds != NULL && ses->cbAIOProcessorR != NULL) { + if (maxfd < ses->fd) + maxfd = ses->fd; + FD_SET(ses->fd, rds); + } + // + if (wrs != NULL && ses->cbAIOProcessorW != NULL) { + if (maxfd < ses->fd) + maxfd = ses->fd; + FD_SET(ses->fd, wrs); + } + // + for (Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) { + if (sam3aIsActiveConnection(c)) { + if (rds != NULL && c->cbAIOProcessorR != NULL) { + if (maxfd < c->fd) + maxfd = c->fd; + FD_SET(c->fd, rds); + } + // + if (wrs != NULL && c->cbAIOProcessorW != NULL) { + if (!c->callDisconnectCB || (c->aio.dataPos < c->aio.dataUsed)) + if (maxfd < c->fd) + maxfd = c->fd; + FD_SET(c->fd, wrs); + } + } + } + } + return maxfd; + } + // + return -1; +} + +void sam3aProcessSessionIO(Sam3ASession *ses, fd_set *rds, fd_set *wrs) { + if (sam3aIsActiveSession(ses)) { + if (ses->fd >= 0 && !ses->cancelled && ses->cbAIOProcessorR != NULL && + rds != NULL && FD_ISSET(ses->fd, rds)) + ses->cbAIOProcessorR(ses); + if (ses->fd >= 0 && !ses->cancelled && ses->cbAIOProcessorW != NULL && + wrs != NULL && FD_ISSET(ses->fd, wrs)) + ses->cbAIOProcessorW(ses); + // + for (Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) { + if (c->fd >= 0 && !c->cancelled && c->cbAIOProcessorR != NULL && + rds != NULL && FD_ISSET(c->fd, rds)) + c->cbAIOProcessorR(c); + if (c->fd >= 0 && !c->cancelled && c->cbAIOProcessorW != NULL && + wrs != NULL && FD_ISSET(c->fd, wrs)) + c->cbAIOProcessorW(c); + } + } +} diff --git a/supportlibs/libsam3/src/libsam3a/libsam3a.h b/supportlibs/libsam3/src/libsam3a/libsam3a.h new file mode 100644 index 000000000..4ddec7952 --- /dev/null +++ b/supportlibs/libsam3/src/libsam3a/libsam3a.h @@ -0,0 +1,361 @@ +/* async SAMv3 library + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#ifndef LIBSAM3A_H +#define LIBSAM3A_H + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//////////////////////////////////////////////////////////////////////////////// +/* + * TODO: + * [.] block sam3aClose*() in callbacks + */ + +//////////////////////////////////////////////////////////////////////////////// +extern int libsam3a_debug; + +//////////////////////////////////////////////////////////////////////////////// +#define SAM3A_HOST_DEFAULT (NULL) +#define SAM3A_PORT_DEFAULT (0) + +#define SAM3A_DESTINATION_TRANSIENT (NULL) + +#define SAM3A_PUBKEY_SIZE (516) +#define SAM3A_PRIVKEY_SIZE (884) + +//////////////////////////////////////////////////////////////////////////////// +extern uint64_t sam3atimeval2ms(const struct timeval *tv); +extern void sam3ams2timeval(struct timeval *tv, uint64_t ms); + +//////////////////////////////////////////////////////////////////////////////// +extern int sam3aIsValidPubKey(const char *key); +extern int sam3aIsValidPrivKey(const char *key); + +//////////////////////////////////////////////////////////////////////////////// +typedef struct Sam3ASession Sam3ASession; +typedef struct Sam3AConnection Sam3AConnection; + +typedef enum { + SAM3A_SESSION_RAW, + SAM3A_SESSION_DGRAM, + SAM3A_SESSION_STREAM +} Sam3ASessionType; + +typedef struct { + char *data; + int dataSize; + int dataUsed; + int dataPos; + void *udata; + union { + void (*cbReplyCheckSes)(Sam3ASession *ses); + void (*cbReplyCheckConn)(Sam3AConnection *conn); + }; +} Sam3AIO; + +/** session callback functions */ +typedef struct { + void (*cbError)(Sam3ASession *ses); /** called on error */ + void (*cbCreated)( + Sam3ASession *ses); /** called when we created the session */ + void (*cbDisconnected)(Sam3ASession *ses); /* call when closed; will called + only after cbCreated() */ + void (*cbDatagramRead)(Sam3ASession *ses, const void *buf, + int bufsize); /* called when we got a datagram; bufsize + >= 0; destkey set */ + void (*cbDestroy)(Sam3ASession *ses); /* called when fd is already closed, but + keys is not cleared */ +} Sam3ASessionCallbacks; + +struct Sam3ASession { + Sam3ASessionType type; /** session type */ + int fd; /** socket file descriptor */ + int cancelled; /** fd was shutdown()ed, but not closed yet */ + char privkey[SAM3A_PRIVKEY_SIZE + 1]; /** private key (asciiz) */ + char pubkey[SAM3A_PUBKEY_SIZE + 1]; /** public key (asciiz) */ + char channel[66]; /** channel name (asciiz) */ + char destkey[SAM3A_PUBKEY_SIZE + 1]; /** for DGRAM sessions (asciiz) */ + char error[64]; /** error message (asciiz) */ + uint32_t ip; /** ipv4 address of sam api interface */ + int port; /** UDP port for DRAM/RAW (can be 0) */ + Sam3AConnection *connlist; /** list of opened connections */ + + /** begin internal members */ + // for async i/o + Sam3AIO aio; + void (*cbAIOProcessorR)(Sam3ASession *ses); // internal + void (*cbAIOProcessorW)(Sam3ASession *ses); // internal + int callDisconnectCB; + char *params; // will be cleared only by sam3aCloseSession() + int timeoutms; + + /** end internal members */ + + Sam3ASessionCallbacks cb; + void *udata; +}; + +/** connection callbacks for data sockets */ +typedef struct { + /** called on error */ + void (*cbError)(Sam3AConnection *ct); + /** called when closed or only after cbConnected()/cbAccepted(); note that + * force disconnect is ok */ + void (*cbDisconnected)(Sam3AConnection *ct); + /** called when connected */ + void (*cbConnected)(Sam3AConnection *ct); + /** called instead of cbConnected() for sam3aStreamAccept*(), destkey filled + * with remote destination */ + void (*cbAccepted)(Sam3AConnection *ct); + /** send callback, data sent, can add new data; will be called after + * connect/accept */ + void (*cbSent)(Sam3AConnection *ct); + /** read callback, data read from socket (bufsize is always > 0) */ + void (*cbRead)(Sam3AConnection *ct, const void *buf, int bufsize); + /** fd already closed, but keys is not cleared */ + void (*cbDestroy)(Sam3AConnection *ct); +} Sam3AConnectionCallbacks; + +struct Sam3AConnection { + /** parent session */ + Sam3ASession *ses; + Sam3AConnection *next; + /** file descriptor */ + int fd; + int cancelled; // fd was shutdown()ed, but not closed yet + char destkey[SAM3A_PUBKEY_SIZE + 1]; // (asciiz) + char error[32]; // (asciiz) + + /** begin internal members */ + // for async i/o + Sam3AIO aio; + void (*cbAIOProcessorR)(Sam3AConnection *ct); // internal + void (*cbAIOProcessorW)(Sam3AConnection *ct); // internal + int callDisconnectCB; + char *params; // will be cleared only by sam3aCloseConnection() + int timeoutms; + /** end internal members */ + + /** callbacks */ + Sam3AConnectionCallbacks cb; + /** user data */ + void *udata; +}; + +//////////////////////////////////////////////////////////////////////////////// +/* + * check if session is active (i.e. have opened socket) + * returns bool + */ +extern int sam3aIsActiveSession(const Sam3ASession *ses); + +/* + * check if connection is active (i.e. have opened socket) + * returns bool + */ +extern int sam3aIsActiveConnection(const Sam3AConnection *conn); + +//////////////////////////////////////////////////////////////////////////////// +/* + * note, that return error codes indicates invalid structure, pointer or fd + * (i.e. immediate errors); all network errors indicated with cbError() callback + */ + +/* + * create SAM session + * pass NULL as hostname for 'localhost' and 0 as port for 7656 + * pass NULL as privkey to create TRANSIENT session + * 'params' can be NULL + * see http://www.i2p2.i2p/i2cp.html#options for common options, + * and http://www.i2p2.i2p/streaming.html#options for STREAM options + * if result<0: error, 'ses' fields are undefined, no need to call + * sam3aCloseSession() if result==0: ok, all 'ses' fields are filled + * TODO: don't clear 'error' field on error (and set it to something meaningful) + */ +extern int sam3aCreateSessionEx(Sam3ASession *ses, + const Sam3ASessionCallbacks *cb, + const char *hostname, int port, + const char *privkey, Sam3ASessionType type, + const char *params, int timeoutms); + +static inline int sam3aCreateSession(Sam3ASession *ses, + const Sam3ASessionCallbacks *cb, + const char *hostname, int port, + const char *privkey, + Sam3ASessionType type) { + return sam3aCreateSessionEx(ses, cb, hostname, port, privkey, type, NULL, -1); +} + +/* returns <0 on error, 0 if no, >0 if yes */ +extern int sam3aIsHaveActiveConnections(const Sam3ASession *ses); + +/* + * close SAM session (and all it's connections) + * returns <0 on error, 0 on ok + * 'ses' must be properly initialized + */ +extern int sam3aCloseSession(Sam3ASession *ses); + +/* + * cancel SAM session (and all it's connections), but don't free() or clear + * anything except fds returns <0 on error, 0 on ok 'ses' must be properly + * initialized + */ +extern int sam3aCancelSession(Sam3ASession *ses); + +/* + * open stream connection to 'destkey' endpoint + * 'destkey' is 516-byte public key (asciiz) + * returns <0 on error + * sets ses->error on memory or socket creation error + */ +extern Sam3AConnection *sam3aStreamConnectEx(Sam3ASession *ses, + const Sam3AConnectionCallbacks *cb, + const char *destkey, + int timeoutms); + +static inline Sam3AConnection * +sam3aStreamConnect(Sam3ASession *ses, const Sam3AConnectionCallbacks *cb, + const char *destkey) { + return sam3aStreamConnectEx(ses, cb, destkey, -1); +} + +/* + * accepts stream connection and sets 'destkey' + * 'destkey' is 516-byte public key + * returns <0 on error, fd on ok + * you still have to call sam3aCloseSession() on failure + * sets ses->error on error + * note that there is no timeouts for now, but you can use sam3atcpSetTimeout*() + */ +extern Sam3AConnection *sam3aStreamAcceptEx(Sam3ASession *ses, + const Sam3AConnectionCallbacks *cb, + int timeoutms); + +static inline Sam3AConnection * +sam3aStreamAccept(Sam3ASession *ses, const Sam3AConnectionCallbacks *cb) { + return sam3aStreamAcceptEx(ses, cb, -1); +} + +/* + * close SAM connection, remove it from session and free memory + * returns <0 on error, 0 on ok + * 'conn' must be properly initialized + * 'conn' is invalid after call + */ +extern int sam3aCloseConnection(Sam3AConnection *conn); + +/* + * cancel SAM connection, but don't free() or clear anything except fd + * returns <0 on error, 0 on ok + * 'conn' must be properly initialized + * 'conn' is invalid after call + */ +extern int sam3aCancelConnection(Sam3AConnection *conn); + +//////////////////////////////////////////////////////////////////////////////// +/* + * send data + * this function can be used in cbSent() callback + * + * return: <0: error; 0: ok + */ +extern int sam3aSend(Sam3AConnection *conn, const void *data, int datasize); + +/* + * sends datagram to 'destkey' endpoint + * 'destkey' is 516-byte public key + * returns <0 on error, 0 on ok + * you still have to call sam3aCloseSession() on failure + * sets ses->error on error + * don't send datagrams bigger than 31KB! + */ +extern int sam3aDatagramSend(Sam3ASession *ses, const char *destkey, + const void *buf, int bufsize); + +//////////////////////////////////////////////////////////////////////////////// +/* + * generate random channel name + * dest should be at least (maxlen+1) bytes big + */ +extern int sam3aGenChannelName(char *dest, int minlen, int maxlen); + +//////////////////////////////////////////////////////////////////////////////// +/* + * generate new keypair + * fills 'privkey' and 'pubkey' only + * you should call sam3aCloseSession() on 'ses' + * cbCreated callback will be called when keys generated + * returns <0 on error, 0 on ok + */ +extern int sam3aGenerateKeysEx(Sam3ASession *ses, + const Sam3ASessionCallbacks *cb, + const char *hostname, int port, int timeoutms); + +static inline int sam3aGenerateKeys(Sam3ASession *ses, + const Sam3ASessionCallbacks *cb, + const char *hostname, int port) { + return sam3aGenerateKeysEx(ses, cb, hostname, port, -1); +} + +/* + * do name lookup (something like gethostbyname()) + * fills 'destkey' only + * you should call sam3aCloseSession() on 'ses' + * cbCreated callback will be called when keys generated, ses->destkey will be + * set returns <0 on error, 0 on ok + */ +extern int sam3aNameLookupEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, + const char *hostname, int port, const char *name, + int timeoutms); + +static inline int sam3aNameLookup(Sam3ASession *ses, + const Sam3ASessionCallbacks *cb, + const char *hostname, int port, + const char *name) { + return sam3aNameLookupEx(ses, cb, hostname, port, name, -1); +} + +//////////////////////////////////////////////////////////////////////////////// +/* + * append session fd to read and write sets if necessary + * adds all alive session connections too + * returns maxfd or -1 + * TODO: should keep fd count so it will not exceed FD_SETSIZE! + */ +extern int sam3aAddSessionToFDS(Sam3ASession *ses, int maxfd, fd_set *rds, + fd_set *wrs); + +/* + * process session i/o (and all session connections i/o) + * should be called after successful select() + */ +extern void sam3aProcessSessionIO(Sam3ASession *ses, fd_set *rds, fd_set *wrs); + +//////////////////////////////////////////////////////////////////////////////// +/* return malloc()ed buffer and len in 'plen' (if plen != NULL) */ +extern char *sam3PrintfVA(int *plen, const char *fmt, va_list app); +extern char *sam3Printf(int *plen, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/supportlibs/libsam3/test/libsam3/test_b32.c b/supportlibs/libsam3/test/libsam3/test_b32.c new file mode 100644 index 000000000..9a852853d --- /dev/null +++ b/supportlibs/libsam3/test/libsam3/test_b32.c @@ -0,0 +1,51 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include + +#include "../../src/ext/tinytest.h" +#include "../../src/ext/tinytest_macros.h" +#include "../../src/libsam3/libsam3.h" + +static int testb32(const char *src, const char *res) { + size_t dlen = sam3Base32EncodedLength(strlen(src)), len; + char dest[128]; + // + len = sam3Base32Encode(dest, sizeof(dest), src, strlen(src)); + tt_int_op(len, ==, dlen); + tt_int_op(len, ==, strlen(res)); + tt_str_op(res, ==, dest); + return 1; + +end: + return 0; +} + +void test_b32_encode(void *data) { + (void)data; /* This testcase takes no data. */ + + tt_assert(testb32("", "")); + tt_assert(testb32("f", "my======")); + tt_assert(testb32("fo", "mzxq====")); + tt_assert(testb32("foo", "mzxw6===")); + tt_assert(testb32("foob", "mzxw6yq=")); + tt_assert(testb32("fooba", "mzxw6ytb")); + tt_assert(testb32("foobar", "mzxw6ytboi======")); + +end:; +} + +struct testcase_t b32_tests[] = {{ + "encode", + test_b32_encode, + }, + END_OF_TESTCASES}; diff --git a/supportlibs/libsam3/test/test.c b/supportlibs/libsam3/test/test.c new file mode 100644 index 000000000..9e7357962 --- /dev/null +++ b/supportlibs/libsam3/test/test.c @@ -0,0 +1,12 @@ +#include + +#include "../src/ext/tinytest.h" +#include "../src/ext/tinytest_macros.h" + +extern struct testcase_t b32_tests[]; + +struct testgroup_t test_groups[] = {{"b32/", b32_tests}, END_OF_GROUPS}; + +int main(int argc, const char **argv) { + return tinytest_main(argc, argv, test_groups); +} From 42bc295d1a3cdd8f568ca01369cca32c34d96000 Mon Sep 17 00:00:00 2001 From: sehraf Date: Tue, 15 Dec 2020 21:42:48 +0100 Subject: [PATCH 02/20] add i2psam3 --- .../src/gui/settings/ServerPage.cpp | 408 +++++++++++------- retroshare-gui/src/gui/settings/ServerPage.h | 30 +- 2 files changed, 266 insertions(+), 172 deletions(-) diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index e17fc9919..62928f701 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -47,6 +47,8 @@ #include #include +#include + #define ICON_STATUS_UNKNOWN ":/images/ledoff1.png" #define ICON_STATUS_WORKING ":/images/yellowled.png" #define ICON_STATUS_OK ":/images/ledon1.png" @@ -66,7 +68,7 @@ // Tabs numbers *after* non relevant tabs are removed. So do not use them to add/remove tabs!! const static uint32_t TAB_HIDDEN_SERVICE_OUTGOING = 0; const static uint32_t TAB_HIDDEN_SERVICE_INCOMING = 1; -const static uint32_t TAB_HIDDEN_SERVICE_I2P_BOB = 2; +const static uint32_t TAB_HIDDEN_SERVICE_I2P = 2; const static uint32_t TAB_NETWORK = 0; const static uint32_t TAB_HIDDEN_SERVICE = 1; @@ -79,14 +81,14 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) : ConfigPage(parent, flags) , manager(NULL), mOngoingConnectivityCheck(-1) , mIsHiddenNode(false), mHiddenType(RS_HIDDEN_TYPE_NONE) - , mBobAccessible(false) + , mSamAccessible(false) , mEventHandlerId(0) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); #ifndef RS_USE_I2P_BOB - ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important. + ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P); // warning: the order of operation here is very important. #endif if(RsAccounts::isHiddenNode()) @@ -166,14 +168,14 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) QObject::connect(ui.filteredIpsTable,SIGNAL(currentCellChanged(int,int,int,int)),this,SLOT(updateSelectedBlackListIP(int,int,int,int))); QObject::connect(ui.whiteListIpsTable,SIGNAL(currentCellChanged(int,int,int,int)),this,SLOT(updateSelectedWhiteListIP(int,int,int,int))); - QObject::connect(ui.pbBobStart, SIGNAL(clicked()), this, SLOT(startBOB())); - QObject::connect(ui.pbBobRestart, SIGNAL(clicked()), this, SLOT(restartBOB())); - QObject::connect(ui.pbBobStop, SIGNAL(clicked()), this, SLOT(stopBOB())); + QObject::connect(ui.pbBobStart, SIGNAL(clicked()), this, SLOT(startSam())); + QObject::connect(ui.pbBobRestart, SIGNAL(clicked()), this, SLOT(restartSam())); + QObject::connect(ui.pbBobStop, SIGNAL(clicked()), this, SLOT(stopSam())); QObject::connect(ui.pbBobGenAddr, SIGNAL(clicked()), this, SLOT(getNewKey())); QObject::connect(ui.pbBobLoadKey, SIGNAL(clicked()), this, SLOT(loadKey())); - QObject::connect(ui.cb_enableBob, SIGNAL(toggled(bool)), this, SLOT(enableBob(bool))); + QObject::connect(ui.cb_enableBob, SIGNAL(toggled(bool)), this, SLOT(enableSam(bool))); - QObject::connect(ui.cbBobAdvanced, SIGNAL(toggled(bool)), this, SLOT(toggleBobAdvancedSettings(bool))); + QObject::connect(ui.cbBobAdvanced, SIGNAL(toggled(bool)), this, SLOT(toggleSamAdvancedSettings(bool))); QObject::connect(ui.sbBobLengthIn, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); QObject::connect(ui.sbBobLengthOut, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); @@ -184,11 +186,11 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) // These two spin boxes are used for the same thing - keep them in sync! QObject::connect(ui.hiddenpage_proxyPort_i2p, SIGNAL(valueChanged(int)), this, SLOT(syncI2PProxyPortNormal(int))); - QObject::connect(ui.hiddenpage_proxyPort_i2p_2, SIGNAL(valueChanged(int)), this, SLOT(syncI2PProxyPortBob(int))); + QObject::connect(ui.hiddenpage_proxyPort_i2p_2, SIGNAL(valueChanged(int)), this, SLOT(syncI2PProxyPortSam(int))); // These two line edits are used for the same thing - keep them in sync! QObject::connect(ui.hiddenpage_proxyAddress_i2p, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrNormal(QString))); - QObject::connect(ui.hiddenpage_proxyAddress_i2p_2, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrBob(QString))); + QObject::connect(ui.hiddenpage_proxyAddress_i2p_2, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrSam(QString))); connect(NotifyQt::getInstance(), SIGNAL(connectionWithoutCert()), this, SLOT(connectionWithoutCert())); @@ -373,7 +375,7 @@ void ServerPage::load() } mIsHiddenNode = (detail.netMode == RS_NETMODE_HIDDEN); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::getSettings, &mBobSettings); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::getSettings, &mSamSettings); loadCommon(); updateStatus(); @@ -870,9 +872,9 @@ void ServerPage::updateStatus() loadFilteredIps() ; - updateStatusBob(); + updateStatusSam(); - // this is used by BOB + // this is used by SAM if (mOngoingConnectivityCheck > 0) { mOngoingConnectivityCheck--; @@ -1349,32 +1351,33 @@ void ServerPage::updateOutProxyIndicator() ui.iconlabel_tor_outgoing->setToolTip(tr("Tor proxy is not enabled")) ; } - // I2P - socket.connectToHost(ui.hiddenpage_proxyAddress_i2p->text(),ui.hiddenpage_proxyPort_i2p->text().toInt()); - if(socket.waitForConnected(500)) - { - socket.disconnectFromHost(); - ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; - ui.iconlabel_i2p_outgoing->setToolTip(tr("Proxy seems to work.")) ; - } - else - { - ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; - ui.iconlabel_i2p_outgoing->setToolTip(tr("I2P proxy is not enabled")) ; - } + // I2P - SAM + // Note: there is only "the SAM port", there is no additional proxy port! + samStatus ss; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::status, &ss); + if(ss.state == samStatus::samState::online) + { + socket.disconnectFromHost(); + ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; + ui.iconlabel_i2p_outgoing->setToolTip(tr("Proxy seems to work.")) ; + } + else + { + ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; + ui.iconlabel_i2p_outgoing->setToolTip(tr("I2P proxy is not enabled")) ; + } - // I2P - BOB - socket.connectToHost(ui.hiddenpage_proxyAddress_i2p_2->text(), 2827); - if(true == (mBobAccessible = socket.waitForConnected(500))) + socket.connectToHost(ui.hiddenpage_proxyAddress_i2p_2->text(), 7656); + if(true == (mSamAccessible = socket.waitForConnected(1000))) { socket.disconnectFromHost(); ui.iconlabel_i2p_outgoing_2->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; - ui.iconlabel_i2p_outgoing_2->setToolTip(tr("BOB is running and accessible")) ; + ui.iconlabel_i2p_outgoing_2->setToolTip(tr("SAMv3 is running and accessible")) ; } else { ui.iconlabel_i2p_outgoing_2->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; - ui.iconlabel_i2p_outgoing_2->setToolTip(tr("BOB is not accessible! Is it running?")) ; + ui.iconlabel_i2p_outgoing_2->setToolTip(tr("SAMv3 is not accessible! Is i2p running and SAM enabled?")) ; } } @@ -1385,28 +1388,18 @@ void ServerPage::updateInProxyIndicator() if(!mIsHiddenNode) return ; - //ui.iconlabel_tor_incoming->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; + //ui.iconlabel_tor_incoming->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; //ui.testIncomingTor_PB->setIcon(FilesDefs::getIconFromQtResourcePath(":/loader/circleball-16.gif")) ; QMovie *movie = new QMovie(":/images/loader/circleball-16.gif"); ui.iconlabel_service_incoming->setMovie(movie); movie->start(); - if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enable) { - - QTcpSocket tcpSocket; - - const QString host = ui.hiddenpage_proxyAddress_i2p->text(); - qint16 port = ui.hiddenpage_proxyPort_i2p->text().toInt(); - QByteArray addr = ui.leBobB32Addr->text().toUtf8(); - addr.push_back('\n'); - - mOngoingConnectivityCheck = 5; // timeout in sec - - tcpSocket.connectToHost(host, port); - tcpSocket.write(addr); // write addr - tcpSocket.write(addr); // trigger connection error since RS expects a tls connection - tcpSocket.close(); - tcpSocket.waitForDisconnected(5 * 1000); + if (mHiddenType == RS_HIDDEN_TYPE_I2P && mSamSettings.enable) { + // there is no inproxy for SAMv3, since every connection goes through sam itself + auto secw = new samEstablishConnectionWrapper(); + secw->address = mSamSettings.address; + secw->connection = nullptr; + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::establishConnection, this, secw); return; } @@ -1419,18 +1412,10 @@ void ServerPage::updateInProxyIndicator() QNetworkProxy proxy ; proxy.setType(QNetworkProxy::Socks5Proxy); - switch (mHiddenType) { - case RS_HIDDEN_TYPE_I2P: - proxy.setHostName(ui.hiddenpage_proxyAddress_i2p->text()); - proxy.setPort(ui.hiddenpage_proxyPort_i2p->text().toInt()); - break; - case RS_HIDDEN_TYPE_TOR: - proxy.setHostName(ui.hiddenpage_proxyAddress_tor->text()); - proxy.setPort(ui.hiddenpage_proxyPort_tor->text().toInt()); - break; - default: - return; - } + + proxy.setHostName(ui.hiddenpage_proxyAddress_tor->text()); + proxy.setPort(ui.hiddenpage_proxyPort_tor->text().toInt()); + proxy.setCapabilities(QNetworkProxy::HostNameLookupCapability | proxy.capabilities()) ; QNetworkProxy::setApplicationProxy(proxy) ; @@ -1445,53 +1430,60 @@ void ServerPage::updateInProxyIndicator() QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy) ; } -void ServerPage::startBOB() +void ServerPage::startSam() { - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::start); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::start); updateStatus(); } -void ServerPage::restartBOB() +void ServerPage::restartSam() { - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::stop); - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::start); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::stop); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::start); updateStatus(); } -void ServerPage::stopBOB() +void ServerPage::stopSam() { - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::stop); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::stop); updateStatus(); } void ServerPage::getNewKey() { - bobSettings *bs = new bobSettings(); - - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::receiveKey, this, bs); + i2p::address *addr = new i2p::address(); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::receiveKey, this, addr); updateStatus(); } void ServerPage::loadKey() { - mBobSettings.address.privateKey = ui.pteBobServerKey->toPlainText().toStdString(); - mBobSettings.address.publicKey = i2p::publicKeyFromPrivate(mBobSettings.address.privateKey); - mBobSettings.address.base32 = i2p::keyToBase32Addr(mBobSettings.address.publicKey); + auto priv = ui.pteBobServerKey->toPlainText().toStdString(); + auto pub = i2p::publicKeyFromPrivate(priv); + if (pub.empty()) { + // something went wrong! + ui.pteBobServerKey->setPlainText("FAILED! Something went wrong while parsing the key!"); + return; + } - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); + mSamSettings.address.privateKey = priv; + mSamSettings.address.publicKey = pub; + mSamSettings.address.base32 = i2p::keyToBase32Addr(mSamSettings.address.publicKey); + + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); } -void ServerPage::enableBob(bool checked) +void ServerPage::enableSam(bool checked) { - mBobSettings.enable = checked; + mSamSettings.enable = checked; - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); - setUpBobElements(); + setUpSamElements(); } int8_t fitRange(int i, int min, int max) { @@ -1513,21 +1505,21 @@ void ServerPage::tunnelSettingsChanged(int) vi = ui.sbBobVarianceIn->value(); vo = ui.sbBobVarianceOut->value(); - mBobSettings.inLength = fitRange(li, 0, 7); - mBobSettings.outLength = fitRange(lo, 0, 7); - mBobSettings.inQuantity = fitRange(qi, 1, 16); - mBobSettings.outQuantity = fitRange(qo, 1, 16); - mBobSettings.inVariance = fitRange(vi, -1, 2); - mBobSettings.outVariance = fitRange(vo, -1, 2); + mSamSettings.inLength = fitRange(li, 0, 7); + mSamSettings.outLength = fitRange(lo, 0, 7); + mSamSettings.inQuantity = fitRange(qi, 1, 16); + mSamSettings.outQuantity = fitRange(qo, 1, 16); + mSamSettings.inVariance = fitRange(vi, -1, 2); + mSamSettings.outVariance = fitRange(vo, -1, 2); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); } -void ServerPage::toggleBobAdvancedSettings(bool checked) +void ServerPage::toggleSamAdvancedSettings(bool checked) { ui.swBobAdvanced->setCurrentIndex(checked ? 1 : 0); - if (!mBobSettings.address.privateKey.empty()) { + if (!mSamSettings.address.privateKey.empty()) { if (checked) { ui.pbBobGenAddr->show(); } else { @@ -1541,13 +1533,13 @@ void ServerPage::syncI2PProxyPortNormal(int i) ui.hiddenpage_proxyPort_i2p_2->setValue(i); } -void ServerPage::syncI2PProxyPortBob(int i) +void ServerPage::syncI2PProxyPortSam(int i) { ui.hiddenpage_proxyPort_i2p->setValue(i); - // update port - saveBob(); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::reloadConfig); + // update port, not necessary for same, we just want to keep it consistent + saveSam(); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::reloadConfig); } void ServerPage::syncI2PProxyAddrNormal(QString t) @@ -1555,38 +1547,70 @@ void ServerPage::syncI2PProxyAddrNormal(QString t) ui.hiddenpage_proxyAddress_i2p_2->setText(t); } -void ServerPage::syncI2PProxyAddrBob(QString t) +void ServerPage::syncI2PProxyAddrSam(QString t) { ui.hiddenpage_proxyAddress_i2p->setText(t); // update addr - saveBob(); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::reloadConfig); + saveSam(); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::reloadConfig); } void ServerPage::taskFinished(taskTicket *&ticket) { - if (ticket->task == autoProxyTask::receiveKey) { - bobSettings *s = NULL; - switch (ticket->types.front()) { - case autoProxyType::I2PBOB: - // update settings - s = (struct bobSettings *)ticket->data; - mBobSettings = *s; - delete s; - s = NULL; - ticket->data = NULL; - break; - default: - break; - } - } + switch (ticket->task) { + case autoProxyTask::receiveKey: + { + i2p::address *addr = nullptr; + addr = static_cast(ticket->data); + + if (ticket->types.front() != autoProxyType::I2PSAM3) + RS_WARN("auto proxy task finished but not for SMA, not exptected! Also not a serious problem."); + else { + // update settings + mSamSettings.address = *addr; + } + + delete addr; + addr = nullptr; + ticket->data = nullptr; + + updateStatusSam(); + break; + } + case autoProxyTask::establishConnection: + { + samEstablishConnectionWrapper *secw = nullptr; + secw = static_cast(ticket->data); + + if (ticket->types.front() != autoProxyType::I2PSAM3) + RS_WARN("auto proxy task finished but not for SMA, not exptected! Also not a serious problem."); + else { + // update settings + if (secw->connection->ses) { + updateInProxyIndicatorResult(true); + sam3CloseConnection(secw->connection); + secw->connection = nullptr; // freed by above call + } else + updateInProxyIndicatorResult(false); + } + + if (secw->connection) + delete secw->connection; + delete secw; + secw = nullptr; + ticket->data = nullptr; + break; + } + default: + RS_DBG("unsupported task!", ticket->task); + } if (ticket->data) std::cerr << "(WW) ServerPage::taskFinished data set. This should NOT happen - check the code!" << std::endl; delete ticket; - ticket = NULL; + ticket = nullptr; } void ServerPage::connectionWithoutCert() @@ -1613,14 +1637,14 @@ void ServerPage::loadCommon() // I2P rsPeers->getProxyServer(RS_HIDDEN_TYPE_I2P, proxyaddr, proxyport, status); whileBlocking(ui.hiddenpage_proxyAddress_i2p) -> setText(QString::fromStdString(proxyaddr)); - whileBlocking(ui.hiddenpage_proxyAddress_i2p_2)->setText(QString::fromStdString(proxyaddr)); // this one is for bob tab + whileBlocking(ui.hiddenpage_proxyAddress_i2p_2)->setText(QString::fromStdString(proxyaddr)); // this one is for sam tab whileBlocking(ui.hiddenpage_proxyPort_i2p) -> setValue(proxyport); - whileBlocking(ui.hiddenpage_proxyPort_i2p_2)->setValue(proxyport); // this one is for bob tab + whileBlocking(ui.hiddenpage_proxyPort_i2p_2)->setValue(proxyport); // this one is for sam tab // don't use whileBlocking here - ui.cb_enableBob->setChecked(mBobSettings.enable); + ui.cb_enableBob->setChecked(mSamSettings.enable); - if (!mBobSettings.address.privateKey.empty()) { + if (!mSamSettings.address.privateKey.empty()) { ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); } @@ -1642,10 +1666,10 @@ void ServerPage::saveCommon() rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, new_proxyaddr, new_proxyport); } - saveBob(); + saveSam(); } -void ServerPage::saveBob() +void ServerPage::saveSam() { std::string orig_proxyaddr, new_proxyaddr; uint16_t orig_proxyport, new_proxyport; @@ -1656,20 +1680,29 @@ void ServerPage::saveBob() new_proxyaddr = ui.hiddenpage_proxyAddress_i2p -> text().toStdString(); new_proxyport = ui.hiddenpage_proxyPort_i2p -> value(); - if ((new_proxyaddr != orig_proxyaddr) || (new_proxyport != orig_proxyport)) { + // SAMv3 has no proxy port, everything goes through the SAM port. + if ((new_proxyaddr != orig_proxyaddr) /* || (new_proxyport != orig_proxyport) */) { rsPeers->setProxyServer(RS_HIDDEN_TYPE_I2P, new_proxyaddr, new_proxyport); } } -void ServerPage::updateStatusBob() +void ServerPage::updateStatusSam() { - QString addr = QString::fromStdString(mBobSettings.address.base32); + QString addr = QString::fromStdString(mSamSettings.address.base32); if (ui.leBobB32Addr->text() != addr) { ui.leBobB32Addr->setText(addr); ui.hiddenpage_serviceAddress->setText(addr); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mSamSettings.address.privateKey)); - if (!mBobSettings.address.privateKey.empty()) { + std::string signingKeyType, cryptoKeyType; + if (i2p::getKeyTypes(mSamSettings.address.publicKey, signingKeyType, cryptoKeyType)) + ui.samKeyInfo->setText(tr("Your key uses the following algorithms: %1 and %2"). + arg(QString::fromStdString(signingKeyType)). + arg(QString::fromStdString(cryptoKeyType))); + else + ui.samKeyInfo->setText(tr("unkown key type")); + + if (!mSamSettings.address.privateKey.empty()) { // we have an addr -> show fields ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); @@ -1689,18 +1722,68 @@ void ServerPage::updateStatusBob() saveAddresses(); } - bobStates bs; - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::status, &bs); + samStatus ss; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::status, &ss); QString bobSimpleText = QString(); - bobSimpleText.append(tr("RetroShare uses BOB to set up a %1 tunnel at %2:%3 (named %4)\n\n" - "When changing options (e.g. port) use the buttons at the bottom to restart BOB.\n\n"). - arg(mBobSettings.address.privateKey.empty() ? tr("client") : tr("server"), + bobSimpleText.append(tr("RetroShare uses SAMv3 to set up a %1 tunnel at %2:%3\n(id: %4)\n\n" + "When changing options (e.g. port) use the buttons at the bottom to restart SAMv3.\n\n"). + arg(mSamSettings.address.privateKey.empty() ? tr("client") : tr("server"), ui.hiddenpage_proxyAddress_i2p_2->text(), - ui.hiddenpage_proxyPort_i2p_2->text(), - bs.tunnelName.empty() ? tr("unknown") : - QString::fromStdString(bs.tunnelName))); + "7656", + ss.sessionName.empty() ? tr("unknown") : + QString::fromStdString(ss.sessionName))); + // update SAM UI based on state + QString s; + QString icon; + switch (ss.state) { + case samStatus::samState::offline: + enableSamElements(false); + + ui.pbBobStart->setEnabled(true); + ui.pbBobRestart->setEnabled(false); + ui.pbBobStop->setEnabled(false); + + s = tr("Offline, no SAM session is established yet.\n"); + icon = ICON_STATUS_ERROR; + break; + case samStatus::samState::connectSession: + enableSamElements(false); + + ui.pbBobStart->setEnabled(false); + ui.pbBobRestart->setEnabled(false); + ui.pbBobStop->setEnabled(true); + + s = tr("SAM is trying to establish a session ... this can take some time.\n"); + icon = ICON_STATUS_WORKING; + break; + case samStatus::samState::connectForward: + enableSamElements(false); + + ui.pbBobStart->setEnabled(false); + ui.pbBobRestart->setEnabled(false); + ui.pbBobStop->setEnabled(true); + + s = tr("SAM session established! Now setting up a forward session ...\n"); + icon = ICON_STATUS_WORKING; + break; + case samStatus::samState::online: + enableSamElements(true); + + ui.pbBobStart->setEnabled(false); + ui.pbBobRestart->setEnabled(true); + ui.pbBobStop->setEnabled(true); + + s = tr("Online, SAM is working as exptected\n"); + icon = ICON_STATUS_OK; + break; + } + ui.iconlabel_i2p_bob->setPixmap(icon); + ui.iconlabel_i2p_bob->setToolTip(s); + bobSimpleText.append(s); + + /* // update BOB UI based on state std::string errorString; switch (bs.cs) { @@ -1711,7 +1794,7 @@ void ServerPage::updateStatusBob() ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); - enableBobElements(false); + enableSamElements(false); { QString s; @@ -1744,7 +1827,7 @@ void ServerPage::updateStatusBob() ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_ERROR)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB is broken\n") + QString::fromStdString(errorString)); - enableBobElements(false); + enableSamElements(false); bobSimpleText.append(tr("BOB encountered an error:\n")); bobSimpleText.append(QString::fromStdString(errorString)); @@ -1760,7 +1843,7 @@ void ServerPage::updateStatusBob() ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is running")); - enableBobElements(false); + enableSamElements(false); bobSimpleText.append(tr("BOB is working fine: tunnel established")); @@ -1773,7 +1856,7 @@ void ServerPage::updateStatusBob() ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); - enableBobElements(false); + enableSamElements(false); bobSimpleText.append(tr("BOB is processing a request")); @@ -1786,7 +1869,7 @@ void ServerPage::updateStatusBob() ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)); ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is not running")); - enableBobElements(true); + enableSamElements(true); bobSimpleText.append(tr("BOB is inactive: tunnel closed")); @@ -1798,16 +1881,18 @@ void ServerPage::updateStatusBob() break; } - ui.pteBobSimple->setPlainText(bobSimpleText); + */ + ui.pteBobSimple->setPlainText(bobSimpleText); + // disable elements when BOB is not accessible - if (!mBobAccessible) { + if (!mSamAccessible) { ui.pbBobStart->setEnabled(false); - ui.pbBobStart->setToolTip("BOB is not accessible"); + ui.pbBobStart->setToolTip("SAMv3 is not accessible"); ui.pbBobRestart->setEnabled(false); - ui.pbBobRestart->setToolTip("BOB is not accessible"); - // don't disable the stop button! (in case bob is running you are otherwise unable to stop and disable it) - ui.pbBobStop->setToolTip("BOB is not accessible"); + ui.pbBobRestart->setToolTip("SAMv3 is not accessible"); + // don't disable the stop button! (in case SAM is running you are otherwise unable to stop and disable it) + ui.pbBobStop->setToolTip("SAMv3 is not accessible"); } else { ui.pbBobStart->setToolTip(""); ui.pbBobRestart->setToolTip(""); @@ -1815,26 +1900,34 @@ void ServerPage::updateStatusBob() } } -void ServerPage::setUpBobElements() +void ServerPage::setUpSamElements() { - ui.gbBob->setEnabled(mBobSettings.enable); - if (mBobSettings.enable) { + ui.gbBob->setEnabled(mSamSettings.enable); + if (mSamSettings.enable) { ui.hiddenpage_proxyAddress_i2p->setEnabled(false); - ui.hiddenpage_proxyAddress_i2p->setToolTip("Use I2P/BOB settings to change this value"); + ui.hiddenpage_proxyAddress_i2p->setToolTip("Use I2P settings to change this value"); ui.hiddenpage_proxyPort_i2p->setEnabled(false); - ui.hiddenpage_proxyPort_i2p->setToolTip("Use I2P/BOB settings to change this value"); + ui.hiddenpage_proxyPort_i2p->setToolTip("Use I2P settings to change this value"); - ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.address.base32)); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); + ui.leBobB32Addr->setText(QString::fromStdString(mSamSettings.address.base32)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mSamSettings.address.privateKey)); + + std::string signingKeyType, cryptoKeyType; + if (i2p::getKeyTypes(mSamSettings.address.publicKey, signingKeyType, cryptoKeyType)) + ui.samKeyInfo->setText(tr("You key uses %1 for signing and %2 for crypto"). + arg(QString::fromStdString(signingKeyType)). + arg(QString::fromStdString(cryptoKeyType))); + else + ui.samKeyInfo->setText(tr("unkown key type")); // cast to int to avoid problems int li, lo, qi, qo, vi, vo; - li = mBobSettings.inLength; - lo = mBobSettings.outLength; - qi = mBobSettings.inQuantity; - qo = mBobSettings.outQuantity; - vi = mBobSettings.inVariance; - vo = mBobSettings.outVariance; + li = mSamSettings.inLength; + lo = mSamSettings.outLength; + qi = mSamSettings.inQuantity; + qo = mSamSettings.outQuantity; + vi = mSamSettings.inVariance; + vo = mSamSettings.outVariance; ui.sbBobLengthIn ->setValue(li); ui.sbBobLengthOut ->setValue(lo); @@ -1850,7 +1943,7 @@ void ServerPage::setUpBobElements() } } -void ServerPage::enableBobElements(bool enable) +void ServerPage::enableSamElements(bool enable) { if (enable) { ui.pbBobGenAddr->setEnabled(true); @@ -1863,13 +1956,13 @@ void ServerPage::enableBobElements(bool enable) ui.cb_enableBob->setToolTip(tr("")); } else { ui.pbBobGenAddr->setEnabled(false); - ui.pbBobGenAddr->setToolTip(tr("stop BOB tunnel first to generate a new key")); + ui.pbBobGenAddr->setToolTip(tr("stop SAM tunnel first to generate a new key")); ui.pbBobLoadKey->setEnabled(false); - ui.pbBobLoadKey->setToolTip(tr("stop BOB tunnel first to load a key")); + ui.pbBobLoadKey->setToolTip(tr("stop SAM tunnel first to load a key")); ui.cb_enableBob->setEnabled(false); - ui.cb_enableBob->setToolTip(tr("stop BOB tunnel first to disable BOB")); + ui.cb_enableBob->setToolTip(tr("stop SAM tunnel first to disable SAM")); } } @@ -1896,6 +1989,7 @@ void ServerPage::handleNetworkReply(QNetworkReply *reply) { int error = reply->error() ; + RS_INFO("error:", error); if(reply->isOpen() && error == QNetworkReply::SslHandshakeFailedError) updateInProxyIndicatorResult(true); else diff --git a/retroshare-gui/src/gui/settings/ServerPage.h b/retroshare-gui/src/gui/settings/ServerPage.h index de971881b..a4bacfa40 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.h +++ b/retroshare-gui/src/gui/settings/ServerPage.h @@ -34,7 +34,7 @@ #endif #include -#include +#include #include "retroshare-gui/configpage.h" #include "retroshare-gui/RsAutoUpdatePage.h" @@ -97,22 +97,22 @@ private slots: void handleNetworkReply(QNetworkReply *reply); void updateInProxyIndicator(); - // i2p bob - void startBOB(); - void restartBOB(); - void stopBOB(); + // i2p SAMv3 + void startSam(); + void restartSam(); + void stopSam(); void getNewKey(); void loadKey(); - void enableBob(bool checked); + void enableSam(bool checked); void tunnelSettingsChanged(int); - void toggleBobAdvancedSettings(bool checked); + void toggleSamAdvancedSettings(bool checked); void syncI2PProxyPortNormal(int i); - void syncI2PProxyPortBob(int i); + void syncI2PProxyPortSam(int i); void syncI2PProxyAddrNormal(QString); - void syncI2PProxyAddrBob(QString); + void syncI2PProxyAddrSam(QString); void connectionWithoutCert(); @@ -133,11 +133,11 @@ public: private: void loadCommon(); void saveCommon(); - void saveBob(); - void updateStatusBob(); + void saveSam(); + void updateStatusSam(); - void setUpBobElements(); - void enableBobElements(bool enable); + void setUpSamElements(); + void enableSamElements(bool enable); void updateInProxyIndicatorResult(bool success); @@ -160,8 +160,8 @@ private: bool mIsHiddenNode; uint32_t mHiddenType; - bobSettings mBobSettings; - bool mBobAccessible; // keeps track wether bob is accessable or not to en/disable the corresponding buttons + samSettings mSamSettings; + bool mSamAccessible; // keeps track wether SAM is accessable or not to en/disable the corresponding buttons RsEventsHandlerId_t mEventHandlerId; void handleEvent(std::shared_ptr event); From 10189ba4d0f5a9e16e0d705097435675c476bec3 Mon Sep 17 00:00:00 2001 From: sehraf Date: Sun, 25 Oct 2020 12:50:55 +0100 Subject: [PATCH 03/20] add i2psam3 --- libretroshare/src/libretroshare.pro | 27 +- libretroshare/src/pqi/pqissl.cc | 1 + libretroshare/src/pqi/pqissli2psam3.cpp | 304 ++++++ libretroshare/src/pqi/pqissli2psam3.h | 53 ++ libretroshare/src/pqi/pqissllistener.h | 4 +- libretroshare/src/pqi/pqisslpersongrp.cc | 20 +- libretroshare/src/retroshare/rsinit.h | 2 +- libretroshare/src/rsserver/p3face.h | 6 +- libretroshare/src/rsserver/rsinit.cc | 51 +- .../src/services/autoproxy/p3i2psam3.cpp | 894 ++++++++++++++++++ .../src/services/autoproxy/p3i2psam3.h | 128 +++ .../services/autoproxy/rsautoproxymonitor.cc | 20 +- .../services/autoproxy/rsautoproxymonitor.h | 24 +- libretroshare/src/use_libretroshare.pri | 9 + libretroshare/src/util/i2pcommon.cpp | 7 +- libretroshare/src/util/i2pcommon.h | 2 +- plugins/plugins.pro | 2 +- retroshare-gui/src/gui/GenCertDialog.cpp | 6 +- retroshare-gui/src/gui/settings/ServerPage.ui | 29 +- retroshare.pri | 21 +- supportlibs/libsam3/src/libsam3/libsam3.c | 18 +- 21 files changed, 1543 insertions(+), 85 deletions(-) create mode 100644 libretroshare/src/pqi/pqissli2psam3.cpp create mode 100644 libretroshare/src/pqi/pqissli2psam3.h create mode 100644 libretroshare/src/services/autoproxy/p3i2psam3.cpp create mode 100644 libretroshare/src/services/autoproxy/p3i2psam3.h diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index 84d18944e..c8f9c4b95 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -157,7 +157,7 @@ rs_webui { HEADERS += plugins/pluginmanager.h \ plugins/dlfcn_win32.h \ - rsitems/rspluginitems.h \ + rsitems/rspluginitems.h \ util/i2pcommon.h \ util/rsinitedptr.h @@ -1014,6 +1014,31 @@ rs_broadcast_discovery { } } +rs_sam3 { + SOURCES += \ + services/autoproxy/p3i2psam3.cpp \ + pqi/pqissli2psam3.cpp \ + + HEADERS += \ + services/autoproxy/p3i2psam3.h \ + pqi/pqissli2psam3.h \ +} + +rs_sam3_libsam3 { + DUMMYQMAKECOMPILERINPUT = FORCE + libsam3.name = Generating libsam3. + libsam3.input = DUMMYQMAKECOMPILERINPUT + libsam3.output = $$clean_path($${LIBSAM3_BUILD_PATH}/libsam3.a) + libsam3.CONFIG += target_predeps combine + libsam3.variable_out = PRE_TARGETDEPS + libsam3.commands = \ + cd $${RS_SRC_PATH} && \ + cp -r $${LIBSAM3_SRC_PATH}/* $${LIBSAM3_BUILD_PATH} && \ + cd $${LIBSAM3_BUILD_PATH} && \ + $(MAKE) build + QMAKE_EXTRA_COMPILERS += libsam3 +} + ########################################################################################################### # OLD CONFIG OPTIONS. # Not used much - but might be useful one day. diff --git a/libretroshare/src/pqi/pqissl.cc b/libretroshare/src/pqi/pqissl.cc index 76d447cc8..8ba82a105 100644 --- a/libretroshare/src/pqi/pqissl.cc +++ b/libretroshare/src/pqi/pqissl.cc @@ -1742,6 +1742,7 @@ bool pqissl::moretoread(uint32_t usec) { rslog(RSL_ALERT, pqisslzone, "pqissl::moretoread() Select ERROR!"); + RS_WARN(errno); return 0; } diff --git a/libretroshare/src/pqi/pqissli2psam3.cpp b/libretroshare/src/pqi/pqissli2psam3.cpp new file mode 100644 index 000000000..ec6497e36 --- /dev/null +++ b/libretroshare/src/pqi/pqissli2psam3.cpp @@ -0,0 +1,304 @@ +#include "pqissli2psam3.h" + +#ifdef RS_USE_I2P_SAM3_I2PSAM +#include "util/i2psam.h" +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 +#include +#endif + +RS_SET_CONTEXT_DEBUG_LEVEL(2) + +static constexpr int pqiDone = 1; +static constexpr int pqiWait = 0; +static constexpr int pqiError = -1; + +pqissli2psam3::pqissli2psam3(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm) + : pqissl(l, parent, lm), mState(pqisslSam3State::NONE), mI2pAddrB32(), mI2pAddrLong() +{ + RS_DBG4(); + mConn = nullptr; +} + +bool pqissli2psam3::connect_parameter(uint32_t type, const std::string &value) +{ + RS_DBG4(); + + if (type == NET_PARAM_CONNECT_DOMAIN_ADDRESS) + { + RS_DBG1("got addr:", value); + RS_STACK_MUTEX(mSslMtx); + mI2pAddrB32 = value; + return true; + } + + return pqissl::connect_parameter(type, value); +} + +int pqissli2psam3::Initiate_Connection() +{ + RS_DBG4(); + + if(waiting != WAITING_DELAY) + { + RS_ERR("Already Attempt in Progress!"); + return pqiError; + } + + switch (mState) { + case(pqisslSam3State::NONE): + RS_DBG2("NONE"); + { + if(mConn) { + // how did we end up here? +#ifdef RS_USE_I2P_SAM3_I2PSAM + unix_close(mConn); +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + sam3CloseConnection(mConn); +#endif + } + mConn = 0; + // get SAM session + mConn = 0; + samSettings ss; + ss.session = nullptr; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::getSettings, static_cast(&ss)); + +#ifdef RS_USE_I2P_SAM3_I2PSAM + if (!!ss.session && !ss.session->isSick()) { +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + if (!!ss.session) { +#endif + RS_DBG3("NONE->DO_LOOKUP"); + mState = pqisslSam3State::DO_LOOKUP; + } else { + RS_DBG3("NONE->DO_LOOKUP NOPE", ss.session); + } + } + break; + case(pqisslSam3State::DO_LOOKUP): + RS_DBG1("DO_LOOKUP"); + + if (!mI2pAddrLong.empty()) { + // skip lookup, it is highly unlikely/impossible for a public key to change (isn't it?) + mState = pqisslSam3State::WAIT_LOOKUP; + break; + } + + { + i2p::address *addr = new i2p::address; + addr->clear(); + addr->base32 = mI2pAddrB32; + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::lookupKey, this, static_cast(addr)); + } + mState = pqisslSam3State::WAIT_LOOKUP; + break; + case(pqisslSam3State::DO_CONNECT): + RS_DBG2("DO_CONNECT"); + + { + auto wrapper = new samEstablishConnectionWrapper(); + wrapper->address.clear(); + wrapper->address.publicKey = mI2pAddrLong; +#ifdef RS_USE_I2P_SAM3_I2PSAM + wrapper->socket = 0; +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + wrapper->connection = nullptr; +#endif + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::establishConnection, this, static_cast(wrapper)); + } + mState = pqisslSam3State::WAIT_CONNECT; + break; + case(pqisslSam3State::DONE): + RS_DBG2("DONE"); + + if (setupSocket()) + return pqiDone; + return pqiError; + + /* waiting */ + case(pqisslSam3State::WAIT_LOOKUP): + RS_DBG3("WAIT_LOOKUP"); + break; + case(pqisslSam3State::WAIT_CONNECT): + RS_DBG3("WAIT_CONNECT"); + break; + } + return pqiWait; +} + +int pqissli2psam3::net_internal_close(int fd) +{ + RS_DBG4(); + + // sanity check +#ifdef RS_USE_I2P_SAM3_I2PSAM + if (mConn && fd != mConn) { +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + if (mConn && fd != mConn->fd) { +#endif + // this should never happen! +//#ifdef RS_USE_I2P_SAM3_I2PSAM +// unix_close(mConn); +//#endif +//#ifdef RS_USE_I2P_SAM3_LIBSAM3 + RS_ERR("fd != mConn"); +// sam3CloseConnection(mConn); +//#endif + } + + // now to the actuall closing + int ret = pqissl::net_internal_close(fd); +// int ret = 0; +//#ifdef RS_USE_I2P_SAM3_LIBSAM3 +// if (mConn) +// ret = sam3CloseConnection(mConn); +//#endif + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::closeConnection, this, mConn), + + // finally cleanup + mConn = 0; + mState = pqisslSam3State::NONE; + + return ret; +} + +void pqissli2psam3::taskFinished(taskTicket *&ticket) +{ + RS_DBG4(); + + switch (ticket->task) { + case autoProxyTask::lookupKey: + { + auto addr = static_cast(ticket->data); + + RS_STACK_MUTEX(mSslMtx); + if (ticket->result == autoProxyStatus::ok) { + mI2pAddrLong = addr->publicKey; + mState = pqisslSam3State::DO_CONNECT; + } else { + waiting = WAITING_FAIL_INTERFACE; + } + + delete addr; + ticket->data = nullptr; + addr = nullptr; + } + break; + case autoProxyTask::establishConnection: + { + auto wrapper = static_cast(ticket->data); + + RS_STACK_MUTEX(mSslMtx); + if (ticket->result == autoProxyStatus::ok) { +#ifdef RS_USE_I2P_SAM3_I2PSAM + mConn = wrapper->socket; +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + mConn = wrapper->connection; +#endif + mState = pqisslSam3State::DONE; + } else { + waiting = WAITING_FAIL_INTERFACE; + } + + delete wrapper; + ticket->data = nullptr; + wrapper = nullptr; + } + break; + case autoProxyTask::closeConnection: + // nothing to do here + break; + default: + RS_WARN("unkown task", ticket->task); + } + + // clean up! + delete ticket; + ticket = nullptr; +} + +bool pqissli2psam3::setupSocket() +{ + /* + * This function contains the generis part from pqissl::Initiate_Connection() + */ + int err; +#ifdef RS_USE_I2P_SAM3_I2PSAM + int osock = mConn; +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + int osock = mConn->fd; +#endif + + err = unix_fcntl_nonblock(osock); + if (err < 0) + { + RS_ERR("Cannot make socket NON-Blocking:", err); + + waiting = WAITING_FAIL_INTERFACE; + net_internal_close(osock); + return false; + } + +#ifdef WINDOWS_SYS + /* Set TCP buffer size for Windows systems */ + + int sockbufsize = 0; + int size = sizeof(int); + + err = getsockopt(osock, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, &size); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: Current TCP receive buffer size " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error getting TCP receive buffer size. Error " << err << std::endl; + } +#endif + + sockbufsize = 0; + + err = getsockopt(osock, SOL_SOCKET, SO_SNDBUF, (char *)&sockbufsize, &size); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: Current TCP send buffer size " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error getting TCP send buffer size. Error " << err << std::endl; + } +#endif + + sockbufsize = WINDOWS_TCP_BUFFER_SIZE; + + err = setsockopt(osock, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, sizeof(sockbufsize)); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: TCP receive buffer size set to " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error setting TCP receive buffer size. Error " << err << std::endl; + } +#endif + + err = setsockopt(osock, SOL_SOCKET, SO_SNDBUF, (char *)&sockbufsize, sizeof(sockbufsize)); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: TCP send buffer size set to " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error setting TCP send buffer size. Error " << err << std::endl; + } +#endif +#endif // WINDOWS_SYS + + + mTimeoutTS = time(NULL) + mConnectTimeout; + //std::cerr << "Setting Connect Timeout " << mConnectTimeout << " Seconds into Future " << std::endl; + + waiting = WAITING_SOCK_CONNECT; + sockfd = osock; + + return true; +} diff --git a/libretroshare/src/pqi/pqissli2psam3.h b/libretroshare/src/pqi/pqissli2psam3.h new file mode 100644 index 000000000..83cba419f --- /dev/null +++ b/libretroshare/src/pqi/pqissli2psam3.h @@ -0,0 +1,53 @@ +#ifndef PQISSLI2PSAM3_H +#define PQISSLI2PSAM3_H + +#include "pqi/pqissl.h" +#include "services/autoproxy/rsautoproxymonitor.h" +#include "services/autoproxy/p3i2psam3.h" + +// Use a state machine as the whole pqi code is designed around them and some operation (like lookup) might be blocking +enum class pqisslSam3State : uint8_t { + NONE = 0, + DO_LOOKUP, + WAIT_LOOKUP, + DO_CONNECT, + WAIT_CONNECT, + DONE +}; + +class pqissli2psam3 : public pqissl, public autoProxyCallback +{ +public: + pqissli2psam3(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm); + + // NetInterface interface +public: + bool connect_parameter(uint32_t type, const std::string &value); + + // pqissl interface +protected: + int Initiate_Connection(); + int net_internal_close(int fd); + + // autoProxyCallback interface +public: + void taskFinished(taskTicket *&ticket); + +private: + bool setupSocket(); + +private: + pqisslSam3State mState; + std::string mI2pAddrB32; + std::string mI2pAddrLong; + +// samSession *mSs; +#ifdef RS_USE_I2P_SAM3_I2PSAM + int mConn; +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + Sam3Connection *mConn; +#endif +}; + +#endif // PQISSLI2PSAM3_H diff --git a/libretroshare/src/pqi/pqissllistener.h b/libretroshare/src/pqi/pqissllistener.h index 85af2d869..800ca876b 100644 --- a/libretroshare/src/pqi/pqissllistener.h +++ b/libretroshare/src/pqi/pqissllistener.h @@ -186,8 +186,8 @@ public: virtual int finaliseConnection(int fd, SSL *ssl, const RsPeerId& peerId, const sockaddr_storage &raddr); + RS_SET_CONTEXT_DEBUG_LEVEL(2) + private: std::map listenaddr; - - RS_SET_CONTEXT_DEBUG_LEVEL(2) }; diff --git a/libretroshare/src/pqi/pqisslpersongrp.cc b/libretroshare/src/pqi/pqisslpersongrp.cc index 7016a2fa6..16112c744 100644 --- a/libretroshare/src/pqi/pqisslpersongrp.cc +++ b/libretroshare/src/pqi/pqisslpersongrp.cc @@ -46,7 +46,7 @@ static struct RsLog::logInfo pqipersongrpzoneInfo = {RsLog::Default, "pqipersong #endif #include "pqi/pqisslproxy.h" -#include "pqi/pqissli2pbob.h" +#include "pqi/pqissli2psam3.h" pqilistener * pqisslpersongrp::locked_createListener(const struct sockaddr_storage &laddr) { @@ -74,26 +74,26 @@ pqiperson * pqisslpersongrp::locked_createPerson(const RsPeerId& id, pqilistener std::cerr << std::endl; #endif - // Use pqicI2PBOB for I2P - pqiconnect *pqicSOCKSProxy, *pqicI2PBOB; + // Use pqicI2P for I2P + pqiconnect *pqicSOCKSProxy, *pqicI2P; { pqisslproxy *pqis = new pqisslproxy((pqissllistener *) listener, pqip, mLinkMgr); RsSerialiser *rss = new RsSerialiser(); rss->addSerialType(new RsRawSerialiser()); pqicSOCKSProxy = new pqiconnect(pqip, rss, pqis); } - if (rsAutoProxyMonitor::instance()->isEnabled(autoProxyType::I2PBOB)) + + if (rsAutoProxyMonitor::instance()->isEnabled(autoProxyType::I2PSAM3)) { - pqissli2pbob *pqis = new pqissli2pbob((pqissllistener *) listener, pqip, mLinkMgr); + pqissli2psam3 *pqis = new pqissli2psam3((pqissllistener *) listener, pqip, mLinkMgr); RsSerialiser *rss = new RsSerialiser(); rss->addSerialType(new RsRawSerialiser()); - pqicI2PBOB = new pqiconnect(pqip, rss, pqis); + pqicI2P = new pqiconnect(pqip, rss, pqis); } else { - pqicI2PBOB = pqicSOCKSProxy; + pqicI2P = pqicSOCKSProxy; } - /* first select type based on peer */ uint32_t typePeer = mPeerMgr->getHiddenType(id); switch (typePeer) { @@ -101,7 +101,7 @@ pqiperson * pqisslpersongrp::locked_createPerson(const RsPeerId& id, pqilistener pqip -> addChildInterface(PQI_CONNECT_HIDDEN_TOR_TCP, pqicSOCKSProxy); break; case RS_HIDDEN_TYPE_I2P: - pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2PBOB); + pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2P); break; default: /* peer is not a hidden one but we are */ @@ -109,7 +109,7 @@ pqiperson * pqisslpersongrp::locked_createPerson(const RsPeerId& id, pqilistener uint32_t typeOwn = mPeerMgr->getHiddenType(AuthSSL::getAuthSSL()->OwnId()); switch (typeOwn) { case RS_HIDDEN_TYPE_I2P: - pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2PBOB); + pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2P); break; default: /* this case shouldn't happen! */ diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index 73fea2684..d26e7ac05 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -195,7 +195,7 @@ public: /* * Setup Hidden Location; */ - static void SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useBob); + static void SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useI2p); static bool LoadPassword(const std::string& passwd) ; diff --git a/libretroshare/src/rsserver/p3face.h b/libretroshare/src/rsserver/p3face.h index 661cb244f..648e39123 100644 --- a/libretroshare/src/rsserver/p3face.h +++ b/libretroshare/src/rsserver/p3face.h @@ -43,7 +43,7 @@ class p3heartbeat; class p3discovery2; -class p3I2pBob; +class p3I2pSam3; /* GXS Classes - just declare the classes. so we don't have to totally recompile to switch */ @@ -161,8 +161,8 @@ public: p3ChatService *chatSrv; p3StatusService *mStatusSrv; p3GxsTunnelService *mGxsTunnels; -#ifdef RS_USE_I2P_BOB - p3I2pBob *mI2pBob; +#ifdef RS_USE_I2P_SAM3 + p3I2pSam3 *mI2pSam3; #endif // This list contains all threaded services. It will be used to shut them down properly. diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 8449a9e3e..864099f01 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -170,7 +170,7 @@ struct RsInitConfig std::string hiddenNodeAddress; uint16_t hiddenNodePort; - bool hiddenNodeI2PBOB; + bool hiddenNodeI2P; /* Logging */ bool haveLogFile; @@ -664,13 +664,13 @@ void RsInit::setAutoLogin(bool autoLogin){ } /* Setup Hidden Location; */ -void RsInit::SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useBob) +void RsInit::SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useI2p) { /* parse the bugger (todo) */ rsInitConfig->hiddenNodeSet = true; rsInitConfig->hiddenNodeAddress = hiddenaddress; rsInitConfig->hiddenNodePort = port; - rsInitConfig->hiddenNodeI2PBOB = useBob; + rsInitConfig->hiddenNodeI2P = useI2p; } @@ -718,6 +718,7 @@ RsGRouter *rsGRouter = NULL ; #endif // def RS_USE_LIBUPNP #include "services/autoproxy/p3i2pbob.h" +#include "services/autoproxy/p3i2psam3.h" #include "services/autoproxy/rsautoproxymonitor.h" #include "services/p3gxsreputation.h" @@ -923,9 +924,9 @@ int RsServer::StartupRetroShare() mNetMgr->setManagers(mPeerMgr, mLinkMgr); rsAutoProxyMonitor *autoProxy = rsAutoProxyMonitor::instance(); -#ifdef RS_USE_I2P_BOB - mI2pBob = new p3I2pBob(mPeerMgr); - autoProxy->addProxy(autoProxyType::I2PBOB, mI2pBob); +#ifdef RS_USE_I2P_SAM3 + mI2pSam3 = new p3I2pSam3(mPeerMgr); + autoProxy->addProxy(autoProxyType::I2PSAM3, mI2pSam3); #endif //load all the SSL certs as friends @@ -1655,8 +1656,9 @@ int RsServer::StartupRetroShare() mConfigMgr->addConfiguration("wire.cfg", wire_ns); #endif #endif //RS_ENABLE_GXS -#ifdef RS_USE_I2P_BOB - mConfigMgr->addConfiguration("I2PBOB.cfg", mI2pBob); +#ifdef RS_USE_I2P_SAM3 + // to make migration easiert, SAM will use BOBs configuration, as they are compatible / the same. + mConfigMgr->addConfiguration("I2PBOB.cfg", mI2pSam3); #endif mPluginsManager->addConfigurations(mConfigMgr) ; @@ -1709,34 +1711,33 @@ int RsServer::StartupRetroShare() { std::cout << "RsServer::StartupRetroShare setting up hidden locations" << std::endl; - if (rsInitConfig->hiddenNodeI2PBOB) { - std::cout << "RsServer::StartupRetroShare setting up BOB" << std::endl; + if (rsInitConfig->hiddenNodeI2P) { + std::cout << "RsServer::StartupRetroShare setting up SAMv3" << std::endl; // we need a local port! mNetMgr->checkNetAddress(); // add i2p proxy - // bob will use this address sockaddr_storage i2pInstance; sockaddr_storage_ipv4_aton(i2pInstance, rsInitConfig->hiddenNodeAddress.c_str()); mPeerMgr->setProxyServerAddress(RS_HIDDEN_TYPE_I2P, i2pInstance); std::string addr; // will be set by auto proxy service - uint16_t port = rsInitConfig->hiddenNodePort; // unused by bob + uint16_t port; // unused by SAM - bool r = autoProxy->initialSetup(autoProxyType::I2PBOB, addr, port); + bool r = autoProxy->initialSetup(autoProxyType::I2PSAM3, addr, port); if (r && !addr.empty()) { mPeerMgr->setupHiddenNode(addr, port); - // now enable bob - bobSettings bs; - autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::getSettings, &bs); - bs.enable = true; - autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &bs); + // now enable SAM + samSettings ss; + autoProxy->taskSync(autoProxyType::I2PSAM3, autoProxyTask::getSettings, &ss); + ss.enable = true; + autoProxy->taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &ss); } else { std::cerr << "RsServer::StartupRetroShare failed to receive keys" << std::endl; - /// TODO add notify for failed bob setup + /// TODO add notify for failed i2p setup } } else { mPeerMgr->setupHiddenNode(rsInitConfig->hiddenNodeAddress, rsInitConfig->hiddenNodePort); @@ -1758,19 +1759,17 @@ int RsServer::StartupRetroShare() if (rsInitConfig->hiddenNodeSet) { // newly created location // mNetMgr->checkNetAddress() will setup ports for us + +#if 0 // this was used for BOB but is not requires for SAMv3 // trigger updates for auto proxy services std::vector types; - - // i2p bob need to rebuild its command map - types.push_back(autoProxyType::I2PBOB); - rsAutoProxyMonitor::taskSync(types, autoProxyTask::reloadConfig); +#endif } /**************************************************************************/ /* startup (stuff dependent on Ids/peers is after this point) */ /**************************************************************************/ - autoProxy->startAll(); pqih->init_listener(); @@ -1803,8 +1802,8 @@ int RsServer::StartupRetroShare() /**************************************************************************/ // auto proxy threads -#ifdef RS_USE_I2P_BOB - startServiceThread(mI2pBob, "I2P-BOB"); +#ifdef RS_USE_I2P_SAM3 + startServiceThread(mI2pSam3, "I2P-SAM3"); #endif #ifdef RS_ENABLE_GXS diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp new file mode 100644 index 000000000..215f23964 --- /dev/null +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -0,0 +1,894 @@ +#include "p3i2psam3.h" + +#ifdef RS_USE_I2P_SAM3_LIBSAM3 +#include +#endif +#ifdef RS_USE_I2P_SAM3_I2PSAM +#include "util/i2psam.h" +#endif + +#include "pqi/p3peermgr.h" +#include "rsitems/rsconfigitems.h" + + +static const std::string kConfigKeySAM3Enable = "SAM3_ENABLE"; + +static const std::string kConfigKeyDestPriv = "DEST_PRIV"; + +static const std::string kConfigKeyInLength = "IN_LENGTH"; +static const std::string kConfigKeyInQuantity = "IN_QUANTITY"; +static const std::string kConfigKeyInVariance = "IN_VARIANCE"; +static const std::string kConfigKeyInBackupQuantity = "IN_BACKUPQUANTITY"; + +static const std::string kConfigKeyOutLength = "OUT_LENGTH"; +static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY"; +static const std::string kConfigKeyOutVariance = "OUT_VARIANCE"; +static const std::string kConfigKeyOutBackupQuantity = "OUT_BACKUPQUANTITY"; + +#ifdef RS_I2P_SAM3_BOB_COMPAT +// used for migration from BOB to SAM +static const std::string kConfigKeyBOBEnable = "BOB_ENABLE"; +static const std::string kConfigKeyBOBKey = "BOB_KEY"; +static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; +#endif + +static constexpr bool kDefaultSAM3Enable = false; + +RS_SET_CONTEXT_DEBUG_LEVEL(4) + +// copy from i2psam.cpp +//#define I2P_DESTINATION_SIZE 516 + +static void inline doSleep(std::chrono::duration> timeToSleepMS) { + std::this_thread::sleep_for(timeToSleepMS); +} + +p3I2pSam3::p3I2pSam3(p3PeerMgr *peerMgr) : + mConfigLoaded(false), mPeerMgr(peerMgr), mPending(), mLock("p3i2p-sam3") +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + , mLockSam3Access("p3i2p-sam3-access") +#endif +{ + RS_DBG4(); + + // set defaults + mSetting.initDefault(); + mSetting.enable = kDefaultSAM3Enable; + mSetting.session = nullptr; + + libsam3_debug = 1; +} + +bool p3I2pSam3::isEnabled() +{ + RS_STACK_MUTEX(mLock); + return mSetting.enable; +} + +bool p3I2pSam3::initialSetup(std::string &addr, uint16_t &/*port*/) +{ + RS_DBG4(); + + RS_STACK_MUTEX(mLock); + + if (!mSetting.address.publicKey.empty() || !mSetting.address.privateKey.empty()) + RS_DBG("overwriting keys!"); + + bool success = generateKey(mSetting.address.publicKey, mSetting.address.privateKey); + + if (!success) { + RS_DBG("failed to retrieve keys"); + return false; + } else { + std::string s, c; + i2p::getKeyTypes(mSetting.address.publicKey, s, c); + RS_INFO("received key", s, c); + + IndicateConfigChanged(); + } + + addr = mSetting.address.base32 = i2p::keyToBase32Addr(mSetting.address.publicKey); + return true; +} + +void p3I2pSam3::processTaskAsync(taskTicket *ticket) +{ + RS_DBG4(); + + switch (ticket->task) { + case autoProxyTask::stop: [[fallthrough]]; + case autoProxyTask::start: [[fallthrough]]; + case autoProxyTask::receiveKey: [[fallthrough]]; + case autoProxyTask::lookupKey: [[fallthrough]]; + case autoProxyTask::proxyStatusCheck: [[fallthrough]]; + case autoProxyTask::establishConnection: [[fallthrough]]; + case autoProxyTask::closeConnection: + { + RS_STACK_MUTEX(mLock); + mPending.push(ticket); + } + break; + case autoProxyTask::status: [[fallthrough]]; + case autoProxyTask::getSettings: [[fallthrough]]; + case autoProxyTask::setSettings: [[fallthrough]]; + case autoProxyTask::getErrorInfo: [[fallthrough]]; + case autoProxyTask::reloadConfig: + // These are supposed to be sync! + RS_DBG("unknown task or sync one!"); + rsAutoProxyMonitor::taskError(ticket); + break; + } +} + +void p3I2pSam3::processTaskSync(taskTicket *ticket) +{ +// RS_DBG4(); + + const bool data = !!ticket->data; + + switch (ticket->task) { + case autoProxyTask::status: + { + samStatus *ss = static_cast(ticket->data); + RS_STACK_MUTEX(mLock); + ss->state = mState; + if (mSetting.session) + ss->sessionName = mSetting.session->channel; + else + ss->sessionName = "none"; + } + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + + break; + case autoProxyTask::getSettings: + // check if everything needed is set + if (!data) { + RS_DBG("autoProxyTask::getSettings data is missing"); + rsAutoProxyMonitor::taskError(ticket); + break; + } + + // get settings + { + RS_STACK_MUTEX(mLock); + *static_cast(ticket->data) = mSetting; + } + + // finish task + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + break; + case autoProxyTask::setSettings: + // check if everything needed is set + if (!data) { + RS_DBG("autoProxyTask::setSettings data is missing"); + rsAutoProxyMonitor::taskError(ticket); + break; + } + + // set settings + { + RS_STACK_MUTEX(mLock); + mSetting = *static_cast(ticket->data); + updateSettings_locked(); + } + + // finish task + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + break; + case autoProxyTask::getErrorInfo: +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + *static_cast(ticket->data) = mSetting.session->error; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); +#else + rsAutoProxyMonitor::taskError(ticket); +#endif + break; + case autoProxyTask::reloadConfig: + { + RS_STACK_MUTEX(mLock); + updateSettings_locked(); + } + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + break; + case autoProxyTask::stop: +#if 0 // doesn't seem to work, socket stays "CLOSE_WAIT" + // there can be a case where libsam3 will block forever because for some reason it does not detect that the socket it has is dead + // as a workaroung kill it from here + if (mState == samStatus::samState::connectSession || mState == samStatus::samState::connectForward) { + // lock should be held by the main thread + if (!mTmpSession) { + // now it's getting weird + RS_WARN("session is nullptr but mState says it is connecting."); + // no break! just ignore for now ... + } else { + // just close it from here, libsam3 is not thread safe. + // a bit of a hack but should do the trick +// sam3CloseSession(mSetting.session); + sam3tcpDisconnect(mTmpSession->fd); + // no break! continue as usual to keep everything in line + } + } +#endif + [[fallthrough]]; + case autoProxyTask::start: [[fallthrough]]; + case autoProxyTask::receiveKey: [[fallthrough]]; + case autoProxyTask::lookupKey: [[fallthrough]]; + case autoProxyTask::proxyStatusCheck: [[fallthrough]]; + case autoProxyTask::establishConnection: [[fallthrough]]; + case autoProxyTask::closeConnection: + // These are supposed to be async! + RS_WARN("unknown task or async one!"); + rsAutoProxyMonitor::taskError(ticket); + break; + } +} + +void p3I2pSam3::threadTick() +{ +// { +// RS_STACK_MUTEX(mLock); +// Dbg4() << __PRETTY_FUNCTION__ << " mPending: " << mPending.size() << std::endl; +// } + + if(mPending.empty()) { + // sleep outisde of lock! + doSleep(std::chrono::milliseconds(250)); + return; + } + + // get task + taskTicket *tt = nullptr; + { + RS_STACK_MUTEX(mLock); + tt = mPending.front(); + mPending.pop(); + } + + switch (tt->task) { + case autoProxyTask::stop: + mState = samStatus::samState::offline; + stopForwarding(); + stopSession(); + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::offline); + break; + + case autoProxyTask::start: + { + if (!mSetting.enable) { + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::disabled); + break; + } + + // create main session + mState = samStatus::samState::connectSession; + bool ret = startSession(); + if (!ret) { + mState = samStatus::samState::offline; + + rsAutoProxyMonitor::taskError(tt); + break; + } + + // start forwarding + mState = samStatus::samState::connectForward; + ret = startForwarding(); + + // finish ticket + if (ret) { + mState = samStatus::samState::online; + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::online); + } else { + mState = samStatus::samState::offline; + rsAutoProxyMonitor::taskError(tt); + } + } + break; + + case autoProxyTask::receiveKey: + { + i2p::address *addr = static_cast(tt->data); + if (generateKey(addr->publicKey, addr->privateKey)) { + addr->base32 = i2p::keyToBase32Addr(addr->publicKey); + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::ok); + } else { + rsAutoProxyMonitor::taskError(tt); + } + } + break; + + case autoProxyTask::lookupKey: + lookupKey(tt); +#ifdef RS_USE_I2P_SAM3_I2PSAM + // artificially delay following operations as i2psam uses time(null) as rng seed + doSleep(std::chrono::seconds(1)); +#endif + break; + + case autoProxyTask::proxyStatusCheck: + { + // TODO better detection of status + bool ok; +#ifdef RS_USE_I2P_SAM3_I2PSAM + ok = !mSetting.session->isSick(); +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + ok = !!mSetting.session->fd; + ok &= !!mSetting.session->fwd_fd; +#endif // RS_USE_I2P_SAM3_LIBSAM3 + *static_cast(tt->data) = ok; + rsAutoProxyMonitor::taskDone(tt, ok ? autoProxyStatus::ok : autoProxyStatus::error); + } + break; + + case autoProxyTask::establishConnection: + establishConnection(tt); + break; + case autoProxyTask::closeConnection: + closeConnection(tt); + break; + case autoProxyTask::status: [[fallthrough]]; + case autoProxyTask::getSettings: [[fallthrough]]; + case autoProxyTask::setSettings: [[fallthrough]]; + case autoProxyTask::getErrorInfo: [[fallthrough]]; + case autoProxyTask::reloadConfig: + RS_ERR("unable to handle! This is a bug! task:", tt->task); + rsAutoProxyMonitor::taskError(tt); + break; + } + tt = nullptr; + + // give i2p backend some time + doSleep(std::chrono::milliseconds(100)); +} + +RsSerialiser *p3I2pSam3::setupSerialiser() +{ + RsSerialiser* rsSerialiser = new RsSerialiser(); + rsSerialiser->addSerialType(new RsGeneralConfigSerialiser()); + + return rsSerialiser; +} + +#define addKVS(_key, _value) \ + kv.key = _key;\ + kv.value = _value;\ + vitem->tlvkvs.pairs.push_back(kv); + +#define addKVSInt(_key, _value) \ + kv.key = _key;\ + rs_sprintf(kv.value, "%d", _value);\ + vitem->tlvkvs.pairs.push_back(kv); + +bool p3I2pSam3::saveList(bool &cleanup, std::list &lst) +{ + RS_DBG4(); + + cleanup = true; + RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; + RsTlvKeyValue kv; + + RS_STACK_MUTEX(mLock); + addKVS(kConfigKeySAM3Enable, mSetting.enable ? "TRUE" : "FALSE") + addKVS(kConfigKeyDestPriv, mSetting.address.privateKey); + + addKVSInt(kConfigKeyInLength, mSetting.inLength) + addKVSInt(kConfigKeyInQuantity, mSetting.inQuantity) + addKVSInt(kConfigKeyInVariance, mSetting.inVariance) + addKVSInt(kConfigKeyInBackupQuantity, mSetting.inBackupQuantity) + + addKVSInt(kConfigKeyOutLength, mSetting.outLength) + addKVSInt(kConfigKeyOutQuantity, mSetting.outQuantity) + addKVSInt(kConfigKeyOutVariance, mSetting.outVariance) + addKVSInt(kConfigKeyOutBackupQuantity, mSetting.outBackupQuantity) + +#ifdef RS_I2P_SAM3_BOB_COMPAT + // these allow SAMv3 users to switch back to BOB + // remove after some time + addKVS(kConfigKeyBOBEnable, mSetting.enable ? "TRUE" : "FALSE") + addKVS(kConfigKeyBOBKey, mSetting.address.privateKey) + addKVS(kConfigKeyBOBAddr, mSetting.address.base32) +#endif + lst.push_back(vitem); + return true; +} + +#undef addKVS +#undef addKVSUInt + +#define getKVSUInt(_kit, _key, _value) \ + else if (_kit->key == _key) {\ + std::istringstream is(_kit->value);\ + int tmp;\ + is >> tmp;\ + _value = (int8_t)tmp;\ + } + +bool p3I2pSam3::loadList(std::list &load) +{ + RS_DBG4(); + + std::string priv; + priv.clear(); + + for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { + RsConfigKeyValueSet *vitem = dynamic_cast(*it); + if(vitem != NULL) { + RS_STACK_MUTEX(mLock); + for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { + if (kit->key == kConfigKeySAM3Enable) + mSetting.enable = kit->value == "TRUE"; + else if (kit->key == kConfigKeyDestPriv) + priv = kit->value; + getKVSUInt(kit, kConfigKeyInLength, mSetting.inLength) + getKVSUInt(kit, kConfigKeyInQuantity, mSetting.inQuantity) + getKVSUInt(kit, kConfigKeyInVariance, mSetting.inVariance) + getKVSUInt(kit, kConfigKeyInBackupQuantity, mSetting.inBackupQuantity) + + getKVSUInt(kit, kConfigKeyOutLength, mSetting.outLength) + getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) + getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) + getKVSUInt(kit, kConfigKeyOutBackupQuantity, mSetting.outBackupQuantity) + +#ifdef RS_I2P_SAM3_BOB_COMPAT + // import BOB settings + else if (kit->key == kConfigKeyBOBEnable) + mSetting.enable = kit->value == "TRUE"; + else if (kit->key == kConfigKeyBOBKey) { + // don't overwirte, just import when not set already! + if (priv.empty()) + priv = kit->value; + } +#endif + else + RS_INFO("unknown key:", kit->key); + } + } + delete vitem; + } + + // get the pub key + std::string pub = i2p::publicKeyFromPrivate(priv); + if (pub.empty() || priv.empty()) + RS_DBG("no destination to load"); + else { + RS_STACK_MUTEX(mLock); + + mSetting.address.publicKey = pub; + mSetting.address.privateKey = priv; + mSetting.address.base32 = i2p::keyToBase32Addr(pub); + } + + RS_STACK_MUTEX(mLock); + mConfigLoaded = true; + + return true; +} + +#undef getKVSUInt + +bool p3I2pSam3::startSession() +{ + RS_DBG4(); + + constexpr size_t len = 8; + const std::string location = RsRandom::alphaNumeric(len); + const std::string nick = "RetroShare-" + location; + + std::vector params; + { + RS_STACK_MUTEX(mLock); + + // length + params.push_back(i2p::makeOption("inbound.length", mSetting.inLength)); + params.push_back(i2p::makeOption("outbound.length", mSetting.outLength)); + // variance + params.push_back(i2p::makeOption("inbound.lengthVariance", + mSetting.inVariance)); + params.push_back(i2p::makeOption("outbound.lengthVariance", + mSetting.outVariance)); + // quantity + params.push_back(i2p::makeOption("inbound.quantity", + mSetting.inQuantity)); + params.push_back(i2p::makeOption("outbound.quantity", + mSetting.outQuantity)); + // backup quantity + params.push_back(i2p::makeOption("inbound.backupQuantity", + mSetting.inBackupQuantity)); + params.push_back(i2p::makeOption("outbound.backupQuantity", + mSetting.outBackupQuantity)); + } + + std::string paramsStr; + for (auto &&p : params) + paramsStr.append(p + " "); + // keep trailing space for easier extending when necessary + +#ifdef RS_USE_I2P_SAM3_I2PSAM + if(mSetting.session) { + RS_STACK_MUTEX(mLock); + + delete mSetting.session; // stopForwardingAll(); is called in destructor + mSetting.session = nullptr; + } + + SAM::StreamSession *session; + + if(!mSetting.address.privateKey.empty()) { + Dbg3() << __PRETTY_FUNCTION__ << " with destination" << std::endl; + session = new SAM::StreamSession(nick, SAM_DEFAULT_ADDRESS, SAM_DEFAULT_PORT, mSetting.address.privateKey, paramsStr); + } else { + Dbg3() << __PRETTY_FUNCTION__ << " without destination" << std::endl; + session = new SAM::StreamSession(nick, SAM_DEFAULT_ADDRESS, SAM_DEFAULT_PORT, SAM_GENERATE_MY_DESTINATION, paramsStr, "DSA_SHA1"); + } + + if (!session || session->isSick()) { + return false; + } + + /* + * i2psam is sometimes unable to reliable read the public key, which is crucial to base32 address generation + * (due to the fact that is assumes it's length wrongly) + * + * The following are attempts to reliable receive our public key + */ + + const auto dest = session->getMyDestination(); +// auto copy = dest; + std::string pubKey1 = dest.pub; + std::string pubKey2(dest.pub); + i2p::validatePubkeyFromPrivKey(pubKey2, dest.priv); + + /* + * pubkeys: + * 1: from initial call (== dest.pub) + * 2: from parsing (was == dest.pub + */ + Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p1 " << pubKey1 << std::endl; + Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p2 " << pubKey2 << std::endl; + if (pubKey1 == pubKey2) { + // unchanged + Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p1 == p2" << std::endl; + } else { + // parsed pub key is different, prefer parsed one + pubKey1 = pubKey2; + Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p1 != p2" << std::endl; + } + SAM::FullDestination dest2(pubKey1, dest.priv, true); + + // populate settings + RS_STACK_MUTEX(mLock); + mSetting.session = session; + if (!mSetting.address.publicKey.empty() && mSetting.address.publicKey != dest2.pub) + // This should be ok for non hidden locations. This should be a problem for hidden i2p locations... + RsDbg() << __PRETTY_FUNCTION__ << " public key changed! Yet unsure if this is ok or a problem" << std::endl; + mSetting.address.publicKey = dest2.pub; + mSetting.address.base32 = i2p::keyToBase32Addr(dest2.pub); + // do not overwrite the private key, if any!! + +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + int ret; + + if (mSetting.session) { + stopSession(); + } + + auto session = new Sam3Session(); + + // add nick + paramsStr.append("inbound.nickname=" + nick); // leading space is already there + + { + RS_STACK_MUTEX(mLockSam3Access); + + if(!mSetting.address.privateKey.empty()) { + RS_DBG3("with destination"); + ret = sam3CreateSilentSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, mSetting.address.privateKey.c_str(), Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); + } else { + RS_DBG("without destination"); + ret = sam3CreateSilentSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, SAM3_DESTINATION_TRANSIENT, Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); + } + } + + if (ret != 0) { + delete session; + session = nullptr; + return false; + } + +#if 0 // this check is useless. For non i2p hidden locations the public key is temporal anyway and for i2p hidden ones, it is part of the (fixed) private key. + if (!mSetting.address.publicKey.empty() && mSetting.address.publicKey != session->pubkey) + // This should be ok for non hidden locations. This should be a problem for hidden i2p locations... + RS_DBG("public key changed! Yet unsure if this is ok or a problem. Should be fine for non i2p hidden locations or clear net."); +#endif + /* + * Note: sam3CreateSession will issue a name looup of "ME" to receive its public key, thus it is always correct. + * No need to use i2p::publicKeyFromPrivate() + */ + RS_STACK_MUTEX(mLock); + mSetting.session = session; + mSetting.address.publicKey = session->pubkey; + mSetting.address.base32 = i2p::keyToBase32Addr(session->pubkey); + // do not overwrite the private key, if any!! +#endif + + RS_DBG1("nick:", nick, "address:", mSetting.address.base32); + RS_DBG2(" myDestination.pub ", mSetting.address.publicKey); + RS_DBG2(" myDestination.priv", mSetting.address.privateKey); + return true; +} + +bool p3I2pSam3::startForwarding() +{ + RS_DBG4(); + + if(mSetting.address.privateKey.empty()) { + RS_DBG3("no private key set"); + // IMPORANT: return true here! + // since there is no forward session for non hidden nodes, this funtion is successfull by doing nothing + return true; + } + + if (!mSetting.session) { + RS_WARN("no session found!"); + return false; + } + + peerState ps; + mPeerMgr->getOwnNetStatus(ps); + +#ifdef RS_USE_I2P_SAM3_I2PSAM + auto ret = mSetting.session->forward(sockaddr_storage_iptostring(ps.localaddr).c_str(), sockaddr_storage_port(ps.localaddr), true); + if (!ret.isOk) + RsDbg() << __PRETTY_FUNCTION__ << " forward failed" << std::endl; + else + Dbg2() << __PRETTY_FUNCTION__ << " forward successfull" << std::endl; + + return ret.isOk; +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + RS_STACK_MUTEX(mLockSam3Access); + + int ret = sam3StreamForward(mSetting.session, sockaddr_storage_iptostring(ps.localaddr).c_str(), sockaddr_storage_port(ps.localaddr)); + + if (ret < 0) { + RS_DBG("forward failed, due to", mSetting.session->error); + return false; + } +#endif + return true; +} + +void p3I2pSam3::stopSession() +{ + RS_DBG4(); + + { + RS_STACK_MUTEX(mLock); + if (!mSetting.session) + return; +#ifdef RS_USE_I2P_SAM3_I2PSAM + // TODO cleanup sockets + delete mSetting.session; // will stop forwarding, too +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + // swap connections + mInvalidConnections = mValidConnections; + mValidConnections.clear(); + + RS_STACK_MUTEX(mLockSam3Access); + sam3CloseSession(mSetting.session); + delete mSetting.session; +#endif + + mSetting.session = nullptr; + mState = samStatus::samState::offline; + } + + // At least i2pd doesn't like to instantaniously stop and (re)start a session, wait here just a little bit. + // Not ideal but does the trick. + // (This happens when using the "restart" button in the settings.) + doSleep(std::chrono::seconds(10)); +} + +void p3I2pSam3::stopForwarding() +{ + // nothing to do here, forwarding is stop when closing the seassion +} + +bool p3I2pSam3::generateKey(std::string &pub, std::string &priv) +{ + RS_DBG4(); + + pub.clear(); + priv.clear(); + +#ifdef RS_USE_I2P_SAM3_I2PSAM + auto ss = new SAM::StreamSession("RS-destgen"); + auto ret = ss->destGenerate(); + if (!ret.isOk) + return false; + + auto dest = ret.value; + pub = std::string(dest.pub); + priv = std::string(dest.priv); + +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + // The session is only usef for transporting the data + Sam3Session ss; + + if (0 > sam3GenerateKeys(&ss, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, Sam3SigType::EdDSA_SHA512_Ed25519)) { + RS_DBG("got error:", ss.error); + return false; + } + pub = std::string(ss.pubkey); + priv = std::string(ss.privkey); +#endif + + RS_DBG2("publuc key / address", pub); + RS_DBG2("private key", priv); + + return true; +} + +void p3I2pSam3::lookupKey(taskTicket *ticket) +{ + // this can be called independend of the main SAM session! + + auto addr = static_cast(ticket->data); + if (addr->base32.empty()) { + RS_ERR("lookupKey: called with empty address"); + rsAutoProxyMonitor::taskError(ticket); + return; + } +#ifdef RS_USE_I2P_SAM3_I2PSAM + auto sam = mSetting.session; + RsThread::async([ticket, sam]() +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + RsThread::async([ticket]() +#endif + { + auto addr = static_cast(ticket->data); + +#ifdef RS_USE_I2P_SAM3_I2PSAM + auto r = sam->namingLookup(addr->base32); + if (!r.isOk) { + // get error + RsDbg() << __PRETTY_FUNCTION__ << " key: " << addr->base32 << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " got error!" << std::endl; + rsAutoProxyMonitor::taskError(ticket); + } else { + addr->publicKey = r.value; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + Dbg1() << __PRETTY_FUNCTION__ << " success " << std::endl; + } +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + // The session is only usef for transporting the data + Sam3Session ss; + int ret = sam3NameLookup(&ss, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, addr->base32.c_str()); + if (ret < 0) { + // get error + RS_DBG("key:", addr->base32); + RS_DBG("got error:", ss.error); + rsAutoProxyMonitor::taskError(ticket); + } else { + addr->publicKey = ss.destkey; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + RS_DBG1("success"); + } +#endif + }); +} + +void p3I2pSam3::establishConnection(taskTicket *ticket) +{ + if (mState != samStatus::samState::online || !mSetting.session) { + RS_WARN("no session found!"); + rsAutoProxyMonitor::taskError(ticket); + return; + } + + samEstablishConnectionWrapper *wrapper = static_cast(ticket->data); + if (wrapper->address.publicKey.empty()) { + RS_ERR("no public key given"); + rsAutoProxyMonitor::taskError(ticket); + return; + } + + RsThread::async([ticket, this]() { + auto wrapper = static_cast(ticket->data); +#ifdef RS_USE_I2P_SAM3_I2PSAM + auto r = this->mSetting.session->connect(wrapper->address.publicKey.c_str(), false); // silent=true is broken! + if (!r.isOk) { + // get error + RsDbg() << __PRETTY_FUNCTION__ << " got error!" << std::endl; + rsAutoProxyMonitor::taskError(ticket); + } else { + // extract socket + wrapper->socket = r.value.get()->release(); + Dbg1() << __PRETTY_FUNCTION__ << " success " << std::endl; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + } +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + struct Sam3Connection *connection; + { + auto l = this->mLockSam3Access; + RS_STACK_MUTEX(l); + connection = sam3StreamConnect(this->mSetting.session, wrapper->address.publicKey.c_str()); + } + + if (!connection) { + // get error + RS_DBG("got error:", this->mSetting.session->error); + rsAutoProxyMonitor::taskError(ticket); + } else { + wrapper->connection = connection; + { + auto l = this->mLockSam3Access; + RS_STACK_MUTEX(l); + this->mValidConnections.push_back(connection); + } + RS_DBG1("success"); + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + } +#endif + }); +} + +void p3I2pSam3::closeConnection(taskTicket *ticket) +{ + Sam3Connection *con = static_cast(ticket->data); + + if (mState == samStatus::samState::offline || !mSetting.session) { + // no session found, sam was likel stopped + } else { + RS_STACK_MUTEX(mLock); + + bool callClose = true; + // search in current connections + auto it = std::find(mValidConnections.begin(), mValidConnections.end(), con); + if (it != mValidConnections.end()) { + mValidConnections.erase(it); + } else { + // search in old connections + it = std::find(mInvalidConnections.begin(), mInvalidConnections.end(), con); + if (it != mInvalidConnections.end()) { + // old connection, just ignore. *should* be freed already + callClose = false; + con = nullptr; + } else { + // weird + RS_WARN("could'n find connection!"); + + // best thing we can do here + callClose = false; + con = nullptr; + } + } + + if (callClose) { + RS_STACK_MUTEX(mLockSam3Access); + sam3CloseConnection(con); + con = nullptr; // freed by above call + } + } + + if (con) { + delete con; + con = nullptr; + } + ticket->data = nullptr; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + return; +} + +void p3I2pSam3::updateSettings_locked() +{ + RS_DBG4(); + IndicateConfigChanged(); + +#if 0 // TODO recreat session when active, can we just recreat it? + if (mSs) { + stopSession(); + startSession(); + } +#endif +} diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.h b/libretroshare/src/services/autoproxy/p3i2psam3.h new file mode 100644 index 000000000..bbf13c143 --- /dev/null +++ b/libretroshare/src/services/autoproxy/p3i2psam3.h @@ -0,0 +1,128 @@ +#ifndef P3I2PSAM3_H +#define P3I2PSAM3_H + +#include +#include + +#include "services/autoproxy/rsautoproxymonitor.h" +#include "pqi/p3cfgmgr.h" +#include "util/i2pcommon.h" +#include "util/rsthreads.h" + +/* + * This class implements I2P SAMv3 (Simple Anonymous Messaging) to allow RS + * to automatically setup tunnel to and from I2P. + * SAMv3 is a simple text-based interface: https://geti2p.net/de/docs/api/samv3 + * + * For the actual SAM commands / low level stuff libsam3 (https://github.com/i2p/libsam3) + * is used with some minor adjustments, for exmaple, the FORWARD session is always silent. + * + * SAM in a nutshell works like this: + * 1) setup main/control session which configures everything (destination ID, tunnel number, hops number, and so on) + * 2) setup a forward session, so that I2P will establish a connection to RS for each incoming connection to our i2p destination + * 3a) query/lookup the destination (public key) for a given i2p address + * 3b) connect to the given destination + * + * An established connection (both incoming or outgoing) are then handed over to RS. + * The lifetime of a session (and its subordinates connections) is bound to their tcp socket. When the socket closes, the session is closed, too. + * + */ + +class p3PeerMgr; + +// typedef samSession is used to unify access to the session independent of the underlying library +#ifdef RS_USE_I2P_SAM3_I2PSAM +namespace SAM { +class StreamSession; +class I2pSocket; +} + +typedef SAM::StreamSession samSession; +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 +class Sam3Session; +class Sam3Connection; + +typedef Sam3Session samSession; +#endif + +struct samSettings : i2p::settings { + samSession *session; +}; + +struct samEstablishConnectionWrapper { + i2p::address address; +#ifdef RS_USE_I2P_SAM3_I2PSAM + int socket; +#endif +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + Sam3Connection *connection; +#endif +}; + +struct samStatus { + std::string sessionName; + enum samState { + offline, + connectSession, + connectForward, + online + } state; // the name is kinda redundant ... +}; + +class p3I2pSam3 : public RsTickingThread, public p3Config, public autoProxyService +{ +public: + p3I2pSam3(p3PeerMgr *peerMgr); + + // autoProxyService interface +public: + bool isEnabled(); + bool initialSetup(std::string &addr, uint16_t &port); + void processTaskAsync(taskTicket *ticket); + void processTaskSync(taskTicket *ticket); + + // RsTickingThread interface +public: + void threadTick(); /// @see RsTickingThread + + // p3Config interface +protected: + RsSerialiser *setupSerialiser(); + bool saveList(bool &cleanup, std::list &); + bool loadList(std::list &load); + +private: + bool startSession(); + bool startForwarding(); + void stopSession(); + void stopForwarding(); + + bool generateKey(std::string &pub, std::string &priv); + void lookupKey(taskTicket *ticket); + void establishConnection(taskTicket *ticket); + void closeConnection(taskTicket *ticket); + void updateSettings_locked(); + + bool mConfigLoaded; + + samSettings mSetting; + p3PeerMgr *mPeerMgr; + std::queue mPending; + + // Used to report the state to the gui + // (Since the create session call/will can block and there is no easy way from outside the main thread to see + // what is going on, it is easier to store the current state in an extra variable independen from the main thread) + samStatus::samState mState; + + // used to keep track of connections, libsam3 does it internally but it can be unreliable since pointers are shared + std::list mValidConnections, mInvalidConnections; + + // mutex + RsMutex mLock; +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + RsMutex mLockSam3Access; // libsam3 is not thread safe! (except for key lookup) +#endif +}; + +#endif // P3I2PSAM3_H diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index d58c871e3..b2d71571e 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -329,14 +329,22 @@ autoProxyService *rsAutoProxyMonitor::lookUpService(autoProxyType::autoProxyType bool rsAutoProxyMonitor::isAsyncTask(autoProxyTask::autoProxyTask_enum t) { + // Explicit list all values, so that missing ones will be detected by the compiler. switch (t) { - case autoProxyTask::start: - case autoProxyTask::stop: - case autoProxyTask::receiveKey: + case autoProxyTask::start: [[fallthrough]]; + case autoProxyTask::stop: [[fallthrough]]; + case autoProxyTask::receiveKey: [[fallthrough]]; + case autoProxyTask::lookupKey: [[fallthrough]]; + case autoProxyTask::establishConnection: [[fallthrough]]; + case autoProxyTask::closeConnection: return true; - break; - default: - break; + case autoProxyTask::status: [[fallthrough]]; + case autoProxyTask::getSettings: [[fallthrough]]; + case autoProxyTask::setSettings: [[fallthrough]]; + case autoProxyTask::getErrorInfo: [[fallthrough]]; + case autoProxyTask::reloadConfig: [[fallthrough]]; + case autoProxyTask::proxyStatusCheck: + return false; } return false; } diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.h b/libretroshare/src/services/autoproxy/rsautoproxymonitor.h index d9a4c16aa..e08692947 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.h +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.h @@ -31,23 +31,27 @@ class autoProxyCallback; namespace autoProxyType { enum autoProxyType_enum { - I2PBOB +// I2PBOB, + I2PSAM3 }; } namespace autoProxyTask { enum autoProxyTask_enum { /* async tasks */ - start, ///< start up proxy - stop, ///< shut down proxy - receiveKey, ///< renew proxy key (if any) - proxyStatusCheck, ///< use to check if the proxy is still running + start, ///< start up proxy + stop, ///< shut down proxy + receiveKey, ///< renew proxy key (if any) + lookupKey, ///< look up a base32 addr + proxyStatusCheck, ///< use to check if the proxy is still running + establishConnection, ///< create a connection to a given public key or base32 address + closeConnection, ///< closes a connection /* sync tasks */ - status, ///< get status from auto proxy - getSettings, ///< get setting from auto proxy - setSettings, ///< set setting of auto proxy - reloadConfig, ///< signal config reload/rebuild - getErrorInfo ///< get error information from auto proxy + status, ///< get status from auto proxy + getSettings, ///< get setting from auto proxy + setSettings, ///< set setting of auto proxy + reloadConfig, ///< signal config reload/rebuild + getErrorInfo ///< get error information from auto proxy }; } diff --git a/libretroshare/src/use_libretroshare.pri b/libretroshare/src/use_libretroshare.pri index b76c4a5da..63009745f 100644 --- a/libretroshare/src/use_libretroshare.pri +++ b/libretroshare/src/use_libretroshare.pri @@ -90,6 +90,15 @@ rs_broadcast_discovery { win32-g++|win32-clang-g++:dLibs *= wsock32 } +rs_sam3_libsam3 { + LIBSAM3_SRC_PATH=$$clean_path($${RS_SRC_PATH}/supportlibs/libsam3/) + LIBSAM3_BUILD_PATH=$$clean_path($${RS_BUILD_PATH}/supportlibs/libsam3/) + INCLUDEPATH *= $$clean_path($${LIBSAM3_SRC_PATH}/src/libsam3/) + DEPENDPATH *= $$clean_path($${LIBSAM3_BUILD_PATH}) + QMAKE_LIBDIR *= $$clean_path($${LIBSAM3_BUILD_PATH}) + LIBS *= -L$$clean_path($${LIBSAM3_BUILD_PATH}) -lsam3 +} + static { sLibs *= $$mLibs } else { diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index bca7ae110..0733a807d 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -50,7 +50,7 @@ std::string publicKeyFromPrivate(std::string const &priv) * https://geti2p.net/spec/common-structures#keysandcert * https://geti2p.net/spec/common-structures#certificate */ - if (priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys) + if (priv.empty() || priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys) return std::string(); // creat a copy to work on, need to convert it to standard base64 @@ -163,6 +163,9 @@ std::string publicKeyFromPrivate(std::string const &priv) bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &cryptoKey) { + if (key.length() < 522) // base64 (391 bytes = 384 bytes + 7 bytes = KeysAndCert + Certificate) + return false; + // creat a copy to work on, need to convert it to standard base64 auto key_copy(key); std::replace(key_copy.begin(), key_copy.end(), '~', '/'); @@ -225,7 +228,7 @@ bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &c // now convert to string (this would be easier with c++17) #define HELPER(a, b, c) \ case static_cast::type>(a::c): \ - b = "c"; \ + b = #c; \ break; switch (signingKeyType) { diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h index e41b61010..0a76fa080 100644 --- a/libretroshare/src/util/i2pcommon.h +++ b/libretroshare/src/util/i2pcommon.h @@ -210,7 +210,7 @@ std::string publicKeyFromPrivate(const std::string &priv); /** * @brief getKeyTypes returns the name of the utilized algorithms used by the key - * @param key public key (private works, too) + * @param key public key * @param signingKey name of the signing key, e.g. DSA_SHA1 * @param cryptoKey name of the crpyto key, e.g. ElGamal * @return true on success, false otherwise diff --git a/plugins/plugins.pro b/plugins/plugins.pro index 209b82898..7ecbc32c7 100644 --- a/plugins/plugins.pro +++ b/plugins/plugins.pro @@ -19,5 +19,5 @@ TEMPLATE = subdirs SUBDIRS += \ - VOIP \ + VOIP \ FeedReader diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index e106146fb..68ff05c42 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -539,12 +539,12 @@ void GenCertDialog::genPerson() std::string hl = ui.hiddenaddr_input->text().toStdString(); uint16_t port = ui.hiddenport_spinBox->value(); - bool useBob = ui.cbUseBob->isChecked(); + bool useI2p = ui.cbUseBob->isChecked(); - if (useBob && hl.empty()) + if (useI2p && hl.empty()) hl = "127.0.0.1"; - RsInit::SetHiddenLocation(hl, port, useBob); /* parses it */ + RsInit::SetHiddenLocation(hl, port, useI2p); /* parses it */ } diff --git a/retroshare-gui/src/gui/settings/ServerPage.ui b/retroshare-gui/src/gui/settings/ServerPage.ui index 5ef7308b1..9d506bf38 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.ui +++ b/retroshare-gui/src/gui/settings/ServerPage.ui @@ -6,8 +6,8 @@ 0 0 - 712 - 502 + 726 + 579 @@ -908,9 +908,9 @@ If you have issues connecting over Tor check the Tor logs too. - + - Automatic I2P/BOB + Automatic I2P @@ -918,7 +918,7 @@ If you have issues connecting over Tor check the Tor logs too. - Enable I2P BOB - changing this requires a restart to fully take effect + Enable I2P SAMv3 - changing this requires a restart to fully take effect @@ -950,7 +950,7 @@ If you have issues connecting over Tor check the Tor logs too. - I2P Basic Open Bridge + I2P Simple Anonymous Messaging @@ -990,6 +990,12 @@ If you have issues connecting over Tor check the Tor logs too. + + false + + + Not required for SAMv3 + 10 @@ -1016,7 +1022,7 @@ If you have issues connecting over Tor check the Tor logs too. <html><head/><body><p>This led is green when the port listen on the left is active on your computer. It does not</p><p>mean that your Retroshare traffic transits though I2P. It will do so only if </p><p>you connect to Hidden nodes, or if you are running a Hidden node yourself.</p></body></html> - BOB accessible + SAM accessible @@ -1304,6 +1310,13 @@ If you have issues connecting over Tor check the Tor logs too. + + + + <key info> + + + @@ -1359,7 +1372,7 @@ If you have issues connecting over Tor check the Tor logs too. - BOB status + SAM status diff --git a/retroshare.pri b/retroshare.pri index 2c2201ef3..825d5d49e 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -140,11 +140,6 @@ rs_macos10.15:CONFIG -= rs_macos10.11 CONFIG *= no_rs_jsonapi rs_jsonapi:CONFIG -= no_rs_jsonapi -# Disable i2p BOB support for automatically setting up an i2p tunnel for RS -# "CONFIG+=no_rs_bob" -CONFIG *= rs_bob -no_rs_bob:CONFIG -= rs_bob - # To enable channel indexing append the following assignation to qmake command # line "CONFIG+=rs_deep_channels_index" CONFIG *= no_rs_deep_channels_index @@ -209,6 +204,10 @@ no_rs_dh_init_check:CONFIG -= rs_dh_init_check # many exported symbols. retroshare_plugins:win32:CONFIG *= libretroshare_shared +CONFIG+=rs_sam3 +CONFIG+=rs_sam3_libsam3 +#CONFIG+=rs_sam3_i2psam + # Specify host precompiled jsonapi-generator path, appending the following # assignation to qmake command line # 'JSONAPI_GENERATOR_EXE=/myBuildDir/jsonapi-generator'. Required for JSON API @@ -559,10 +558,6 @@ rs_webui { DEFINES *= RS_WEBUI } -rs_bob { - DEFINES *= RS_USE_I2P_BOB -} - rs_deep_channels_index:DEFINES *= RS_DEEP_CHANNEL_INDEX rs_deep_files_index:DEFINES *= RS_DEEP_FILES_INDEX @@ -576,6 +571,14 @@ rs_broadcast_discovery:DEFINES *= RS_BROADCAST_DISCOVERY no_rs_dh_init_check:DEFINES *= RS_DISABLE_DIFFIE_HELLMAN_INIT_CHECK +rs_sam3: { + DEFINES *= RS_USE_I2P_SAM3 + # this allows a downgrade from a SAMv3 build to a BOB build, can be removed in the future + DEFINES *= RS_I2P_SAM3_BOB_COMPAT +} +rs_sam3_libsam3: DEFINES *= RS_USE_I2P_SAM3_LIBSAM3 +rs_sam3_i2psam: DEFINES *= RS_USE_I2P_SAM3_I2PSAM + debug { rs_mutex_debug:DEFINES *= RS_MUTEX_DEBUG diff --git a/supportlibs/libsam3/src/libsam3/libsam3.c b/supportlibs/libsam3/src/libsam3/libsam3.c index 367949a39..476372a0b 100644 --- a/supportlibs/libsam3/src/libsam3/libsam3.c +++ b/supportlibs/libsam3/src/libsam3/libsam3.c @@ -26,6 +26,10 @@ #include #include +#ifdef WINDOWS_SYS +#include +#endif // WINDOWS_SYS + //////////////////////////////////////////////////////////////////////////////// int libsam3_debug = 0; @@ -100,6 +104,11 @@ int sam3tcpConnectIP(uint32_t ip, int port) { } } // + // Set this for all outgoing SAM connections. Most SAM commands should be answered rather fast except CREATE SESSION maybe. + // This should be enough to let SAM establish a session. + sam3tcpSetTimeoutSend(fd, 5 * 60 * 1000); + sam3tcpSetTimeoutReceive(fd, 5 * 60 * 1000); + // setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); // if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { @@ -152,8 +161,13 @@ int sam3tcpConnect(const char *hostname, int port, uint32_t *ip) { // <0: error; 0: ok int sam3tcpDisconnect(int fd) { if (fd >= 0) { - shutdown(fd, SHUT_RDWR); - return close(fd); +#ifndef WINDOWS_SYS // ie UNIX + shutdown(fd, SHUT_RDWR); + return close(fd); +#else + return closesocket(fd); +#endif + } // return -1; From eb0aa340e3cfa254c4a9851e035b3501271c067e Mon Sep 17 00:00:00 2001 From: sehraf Date: Wed, 4 Nov 2020 17:47:29 +0100 Subject: [PATCH 04/20] remove i2psam (library) support --- libretroshare/src/pqi/pqissli2psam3.cpp | 50 +----- libretroshare/src/pqi/pqissli2psam3.h | 6 - .../src/services/autoproxy/p3i2psam3.cpp | 156 +----------------- .../src/services/autoproxy/p3i2psam3.h | 18 -- retroshare.pri | 2 - 5 files changed, 7 insertions(+), 225 deletions(-) diff --git a/libretroshare/src/pqi/pqissli2psam3.cpp b/libretroshare/src/pqi/pqissli2psam3.cpp index ec6497e36..41087a893 100644 --- a/libretroshare/src/pqi/pqissli2psam3.cpp +++ b/libretroshare/src/pqi/pqissli2psam3.cpp @@ -1,11 +1,6 @@ #include "pqissli2psam3.h" -#ifdef RS_USE_I2P_SAM3_I2PSAM -#include "util/i2psam.h" -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 #include -#endif RS_SET_CONTEXT_DEBUG_LEVEL(2) @@ -51,12 +46,7 @@ int pqissli2psam3::Initiate_Connection() { if(mConn) { // how did we end up here? -#ifdef RS_USE_I2P_SAM3_I2PSAM - unix_close(mConn); -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 - sam3CloseConnection(mConn); -#endif + RS_ERR("state is NONE but a connection is existing?!"); } mConn = 0; // get SAM session @@ -65,12 +55,7 @@ int pqissli2psam3::Initiate_Connection() ss.session = nullptr; rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::getSettings, static_cast(&ss)); -#ifdef RS_USE_I2P_SAM3_I2PSAM - if (!!ss.session && !ss.session->isSick()) { -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 if (!!ss.session) { -#endif RS_DBG3("NONE->DO_LOOKUP"); mState = pqisslSam3State::DO_LOOKUP; } else { @@ -102,12 +87,8 @@ int pqissli2psam3::Initiate_Connection() auto wrapper = new samEstablishConnectionWrapper(); wrapper->address.clear(); wrapper->address.publicKey = mI2pAddrLong; -#ifdef RS_USE_I2P_SAM3_I2PSAM - wrapper->socket = 0; -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 wrapper->connection = nullptr; -#endif + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::establishConnection, this, static_cast(wrapper)); } mState = pqisslSam3State::WAIT_CONNECT; @@ -135,29 +116,14 @@ int pqissli2psam3::net_internal_close(int fd) RS_DBG4(); // sanity check -#ifdef RS_USE_I2P_SAM3_I2PSAM - if (mConn && fd != mConn) { -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 if (mConn && fd != mConn->fd) { -#endif // this should never happen! -//#ifdef RS_USE_I2P_SAM3_I2PSAM -// unix_close(mConn); -//#endif -//#ifdef RS_USE_I2P_SAM3_LIBSAM3 RS_ERR("fd != mConn"); // sam3CloseConnection(mConn); -//#endif } // now to the actuall closing - int ret = pqissl::net_internal_close(fd); -// int ret = 0; -//#ifdef RS_USE_I2P_SAM3_LIBSAM3 -// if (mConn) -// ret = sam3CloseConnection(mConn); -//#endif + int ret = pqissl::net_internal_close(fd); rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::closeConnection, this, mConn), // finally cleanup @@ -195,12 +161,7 @@ void pqissli2psam3::taskFinished(taskTicket *&ticket) RS_STACK_MUTEX(mSslMtx); if (ticket->result == autoProxyStatus::ok) { -#ifdef RS_USE_I2P_SAM3_I2PSAM - mConn = wrapper->socket; -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 mConn = wrapper->connection; -#endif mState = pqisslSam3State::DONE; } else { waiting = WAITING_FAIL_INTERFACE; @@ -229,12 +190,7 @@ bool pqissli2psam3::setupSocket() * This function contains the generis part from pqissl::Initiate_Connection() */ int err; -#ifdef RS_USE_I2P_SAM3_I2PSAM - int osock = mConn; -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 int osock = mConn->fd; -#endif err = unix_fcntl_nonblock(osock); if (err < 0) diff --git a/libretroshare/src/pqi/pqissli2psam3.h b/libretroshare/src/pqi/pqissli2psam3.h index 83cba419f..90fdff6e0 100644 --- a/libretroshare/src/pqi/pqissli2psam3.h +++ b/libretroshare/src/pqi/pqissli2psam3.h @@ -41,13 +41,7 @@ private: std::string mI2pAddrB32; std::string mI2pAddrLong; -// samSession *mSs; -#ifdef RS_USE_I2P_SAM3_I2PSAM - int mConn; -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 Sam3Connection *mConn; -#endif }; #endif // PQISSLI2PSAM3_H diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp index 215f23964..ed2b51ccd 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.cpp +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -1,11 +1,6 @@ #include "p3i2psam3.h" -#ifdef RS_USE_I2P_SAM3_LIBSAM3 #include -#endif -#ifdef RS_USE_I2P_SAM3_I2PSAM -#include "util/i2psam.h" -#endif #include "pqi/p3peermgr.h" #include "rsitems/rsconfigitems.h" @@ -36,9 +31,6 @@ static constexpr bool kDefaultSAM3Enable = false; RS_SET_CONTEXT_DEBUG_LEVEL(4) -// copy from i2psam.cpp -//#define I2P_DESTINATION_SIZE 516 - static void inline doSleep(std::chrono::duration> timeToSleepMS) { std::this_thread::sleep_for(timeToSleepMS); } @@ -176,12 +168,8 @@ void p3I2pSam3::processTaskSync(taskTicket *ticket) rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); break; case autoProxyTask::getErrorInfo: -#ifdef RS_USE_I2P_SAM3_LIBSAM3 *static_cast(ticket->data) = mSetting.session->error; rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); -#else - rsAutoProxyMonitor::taskError(ticket); -#endif break; case autoProxyTask::reloadConfig: { @@ -298,23 +286,14 @@ void p3I2pSam3::threadTick() case autoProxyTask::lookupKey: lookupKey(tt); -#ifdef RS_USE_I2P_SAM3_I2PSAM - // artificially delay following operations as i2psam uses time(null) as rng seed - doSleep(std::chrono::seconds(1)); -#endif break; case autoProxyTask::proxyStatusCheck: { // TODO better detection of status bool ok; -#ifdef RS_USE_I2P_SAM3_I2PSAM - ok = !mSetting.session->isSick(); -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 ok = !!mSetting.session->fd; ok &= !!mSetting.session->fwd_fd; -#endif // RS_USE_I2P_SAM3_LIBSAM3 *static_cast(tt->data) = ok; rsAutoProxyMonitor::taskDone(tt, ok ? autoProxyStatus::ok : autoProxyStatus::error); } @@ -497,70 +476,6 @@ bool p3I2pSam3::startSession() paramsStr.append(p + " "); // keep trailing space for easier extending when necessary -#ifdef RS_USE_I2P_SAM3_I2PSAM - if(mSetting.session) { - RS_STACK_MUTEX(mLock); - - delete mSetting.session; // stopForwardingAll(); is called in destructor - mSetting.session = nullptr; - } - - SAM::StreamSession *session; - - if(!mSetting.address.privateKey.empty()) { - Dbg3() << __PRETTY_FUNCTION__ << " with destination" << std::endl; - session = new SAM::StreamSession(nick, SAM_DEFAULT_ADDRESS, SAM_DEFAULT_PORT, mSetting.address.privateKey, paramsStr); - } else { - Dbg3() << __PRETTY_FUNCTION__ << " without destination" << std::endl; - session = new SAM::StreamSession(nick, SAM_DEFAULT_ADDRESS, SAM_DEFAULT_PORT, SAM_GENERATE_MY_DESTINATION, paramsStr, "DSA_SHA1"); - } - - if (!session || session->isSick()) { - return false; - } - - /* - * i2psam is sometimes unable to reliable read the public key, which is crucial to base32 address generation - * (due to the fact that is assumes it's length wrongly) - * - * The following are attempts to reliable receive our public key - */ - - const auto dest = session->getMyDestination(); -// auto copy = dest; - std::string pubKey1 = dest.pub; - std::string pubKey2(dest.pub); - i2p::validatePubkeyFromPrivKey(pubKey2, dest.priv); - - /* - * pubkeys: - * 1: from initial call (== dest.pub) - * 2: from parsing (was == dest.pub - */ - Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p1 " << pubKey1 << std::endl; - Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p2 " << pubKey2 << std::endl; - if (pubKey1 == pubKey2) { - // unchanged - Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p1 == p2" << std::endl; - } else { - // parsed pub key is different, prefer parsed one - pubKey1 = pubKey2; - Dbg3() << __PRETTY_FUNCTION__ << " figuring out our pubKey: p1 != p2" << std::endl; - } - SAM::FullDestination dest2(pubKey1, dest.priv, true); - - // populate settings - RS_STACK_MUTEX(mLock); - mSetting.session = session; - if (!mSetting.address.publicKey.empty() && mSetting.address.publicKey != dest2.pub) - // This should be ok for non hidden locations. This should be a problem for hidden i2p locations... - RsDbg() << __PRETTY_FUNCTION__ << " public key changed! Yet unsure if this is ok or a problem" << std::endl; - mSetting.address.publicKey = dest2.pub; - mSetting.address.base32 = i2p::keyToBase32Addr(dest2.pub); - // do not overwrite the private key, if any!! - -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 int ret; if (mSetting.session) { @@ -604,7 +519,6 @@ bool p3I2pSam3::startSession() mSetting.address.publicKey = session->pubkey; mSetting.address.base32 = i2p::keyToBase32Addr(session->pubkey); // do not overwrite the private key, if any!! -#endif RS_DBG1("nick:", nick, "address:", mSetting.address.base32); RS_DBG2(" myDestination.pub ", mSetting.address.publicKey); @@ -631,16 +545,6 @@ bool p3I2pSam3::startForwarding() peerState ps; mPeerMgr->getOwnNetStatus(ps); -#ifdef RS_USE_I2P_SAM3_I2PSAM - auto ret = mSetting.session->forward(sockaddr_storage_iptostring(ps.localaddr).c_str(), sockaddr_storage_port(ps.localaddr), true); - if (!ret.isOk) - RsDbg() << __PRETTY_FUNCTION__ << " forward failed" << std::endl; - else - Dbg2() << __PRETTY_FUNCTION__ << " forward successfull" << std::endl; - - return ret.isOk; -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 RS_STACK_MUTEX(mLockSam3Access); int ret = sam3StreamForward(mSetting.session, sockaddr_storage_iptostring(ps.localaddr).c_str(), sockaddr_storage_port(ps.localaddr)); @@ -649,7 +553,7 @@ bool p3I2pSam3::startForwarding() RS_DBG("forward failed, due to", mSetting.session->error); return false; } -#endif + return true; } @@ -661,11 +565,7 @@ void p3I2pSam3::stopSession() RS_STACK_MUTEX(mLock); if (!mSetting.session) return; -#ifdef RS_USE_I2P_SAM3_I2PSAM - // TODO cleanup sockets - delete mSetting.session; // will stop forwarding, too -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 + // swap connections mInvalidConnections = mValidConnections; mValidConnections.clear(); @@ -673,7 +573,6 @@ void p3I2pSam3::stopSession() RS_STACK_MUTEX(mLockSam3Access); sam3CloseSession(mSetting.session); delete mSetting.session; -#endif mSetting.session = nullptr; mState = samStatus::samState::offline; @@ -697,18 +596,6 @@ bool p3I2pSam3::generateKey(std::string &pub, std::string &priv) pub.clear(); priv.clear(); -#ifdef RS_USE_I2P_SAM3_I2PSAM - auto ss = new SAM::StreamSession("RS-destgen"); - auto ret = ss->destGenerate(); - if (!ret.isOk) - return false; - - auto dest = ret.value; - pub = std::string(dest.pub); - priv = std::string(dest.priv); - -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 // The session is only usef for transporting the data Sam3Session ss; @@ -718,7 +605,6 @@ bool p3I2pSam3::generateKey(std::string &pub, std::string &priv) } pub = std::string(ss.pubkey); priv = std::string(ss.privkey); -#endif RS_DBG2("publuc key / address", pub); RS_DBG2("private key", priv); @@ -736,30 +622,11 @@ void p3I2pSam3::lookupKey(taskTicket *ticket) rsAutoProxyMonitor::taskError(ticket); return; } -#ifdef RS_USE_I2P_SAM3_I2PSAM - auto sam = mSetting.session; - RsThread::async([ticket, sam]() -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 + RsThread::async([ticket]() -#endif { auto addr = static_cast(ticket->data); -#ifdef RS_USE_I2P_SAM3_I2PSAM - auto r = sam->namingLookup(addr->base32); - if (!r.isOk) { - // get error - RsDbg() << __PRETTY_FUNCTION__ << " key: " << addr->base32 << std::endl; - RsDbg() << __PRETTY_FUNCTION__ << " got error!" << std::endl; - rsAutoProxyMonitor::taskError(ticket); - } else { - addr->publicKey = r.value; - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - Dbg1() << __PRETTY_FUNCTION__ << " success " << std::endl; - } -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 // The session is only usef for transporting the data Sam3Session ss; int ret = sam3NameLookup(&ss, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, addr->base32.c_str()); @@ -773,7 +640,6 @@ void p3I2pSam3::lookupKey(taskTicket *ticket) rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); RS_DBG1("success"); } -#endif }); } @@ -794,20 +660,7 @@ void p3I2pSam3::establishConnection(taskTicket *ticket) RsThread::async([ticket, this]() { auto wrapper = static_cast(ticket->data); -#ifdef RS_USE_I2P_SAM3_I2PSAM - auto r = this->mSetting.session->connect(wrapper->address.publicKey.c_str(), false); // silent=true is broken! - if (!r.isOk) { - // get error - RsDbg() << __PRETTY_FUNCTION__ << " got error!" << std::endl; - rsAutoProxyMonitor::taskError(ticket); - } else { - // extract socket - wrapper->socket = r.value.get()->release(); - Dbg1() << __PRETTY_FUNCTION__ << " success " << std::endl; - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - } -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 + struct Sam3Connection *connection; { auto l = this->mLockSam3Access; @@ -829,7 +682,6 @@ void p3I2pSam3::establishConnection(taskTicket *ticket) RS_DBG1("success"); rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); } -#endif }); } diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.h b/libretroshare/src/services/autoproxy/p3i2psam3.h index bbf13c143..c6b57615e 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.h +++ b/libretroshare/src/services/autoproxy/p3i2psam3.h @@ -30,21 +30,10 @@ class p3PeerMgr; -// typedef samSession is used to unify access to the session independent of the underlying library -#ifdef RS_USE_I2P_SAM3_I2PSAM -namespace SAM { -class StreamSession; -class I2pSocket; -} - -typedef SAM::StreamSession samSession; -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 class Sam3Session; class Sam3Connection; typedef Sam3Session samSession; -#endif struct samSettings : i2p::settings { samSession *session; @@ -52,12 +41,7 @@ struct samSettings : i2p::settings { struct samEstablishConnectionWrapper { i2p::address address; -#ifdef RS_USE_I2P_SAM3_I2PSAM - int socket; -#endif -#ifdef RS_USE_I2P_SAM3_LIBSAM3 Sam3Connection *connection; -#endif }; struct samStatus { @@ -120,9 +104,7 @@ private: // mutex RsMutex mLock; -#ifdef RS_USE_I2P_SAM3_LIBSAM3 RsMutex mLockSam3Access; // libsam3 is not thread safe! (except for key lookup) -#endif }; #endif // P3I2PSAM3_H diff --git a/retroshare.pri b/retroshare.pri index 825d5d49e..da7a451c8 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -206,7 +206,6 @@ retroshare_plugins:win32:CONFIG *= libretroshare_shared CONFIG+=rs_sam3 CONFIG+=rs_sam3_libsam3 -#CONFIG+=rs_sam3_i2psam # Specify host precompiled jsonapi-generator path, appending the following # assignation to qmake command line @@ -577,7 +576,6 @@ rs_sam3: { DEFINES *= RS_I2P_SAM3_BOB_COMPAT } rs_sam3_libsam3: DEFINES *= RS_USE_I2P_SAM3_LIBSAM3 -rs_sam3_i2psam: DEFINES *= RS_USE_I2P_SAM3_I2PSAM debug { rs_mutex_debug:DEFINES *= RS_MUTEX_DEBUG From e4336a8a417ecc61dc25c8d471233fca5049d9b4 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 6 Nov 2020 15:45:47 +0100 Subject: [PATCH 05/20] GUI: remove i2p proxy port, there is none with SAM --- retroshare-gui/src/gui/GenCertDialog.cpp | 10 +++---- retroshare-gui/src/gui/GenCertDialog.h | 2 +- retroshare-gui/src/gui/GenCertDialog.ui | 4 +-- .../src/gui/settings/ServerPage.cpp | 21 +------------- retroshare-gui/src/gui/settings/ServerPage.h | 3 -- retroshare-gui/src/gui/settings/ServerPage.ui | 28 ++++++------------- 6 files changed, 18 insertions(+), 50 deletions(-) diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index 68ff05c42..2aae87d09 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -150,7 +150,7 @@ GenCertDialog::GenCertDialog(bool onlyGenerateIdentity, QWidget *parent) connect(ui.node_input, SIGNAL(textChanged(QString)), this, SLOT(updateCheckLabels())); connect(ui.reuse_existing_node_CB, SIGNAL(toggled(bool)), this, SLOT(updateCheckLabels())); - connect(ui.cbUseBob, SIGNAL(clicked(bool)), this, SLOT(useBobChecked(bool)));; + connect(ui.cbUseBob, SIGNAL(clicked(bool)), this, SLOT(useI2pChecked(bool)));; entropy_timer = new QTimer ; entropy_timer->start(20) ; @@ -341,9 +341,9 @@ void GenCertDialog::setupState() ui.hiddenport_spinBox->setVisible(hidden_state && !tor_auto); ui.cbUseBob->setVisible(hidden_state && !tor_auto); -#ifndef RS_USE_I2P_BOB +#ifndef RS_USE_I2P_SAM3 ui.cbUseBob->setDisabled(true); - ui.cbUseBob->setToolTip(tr("BOB support is not available")); + ui.cbUseBob->setToolTip(tr("SAMv3 support is not available")); #endif if(!mAllFieldsOk) @@ -440,10 +440,10 @@ void GenCertDialog::updateCheckLabels() setupState(); } -void GenCertDialog::useBobChecked(bool checked) +void GenCertDialog::useI2pChecked(bool checked) { if (checked) { - ui.hiddenaddr_input->setPlaceholderText(tr("I2P instance address with BOB enabled")); + ui.hiddenaddr_input->setPlaceholderText(tr("I2P instance address with SAMv3 enabled")); ui.hiddenaddr_label->setText(tr("I2P instance address")); ui.hiddenport_spinBox->setEnabled(false); diff --git a/retroshare-gui/src/gui/GenCertDialog.h b/retroshare-gui/src/gui/GenCertDialog.h index afd82e5f0..5bef44485 100644 --- a/retroshare-gui/src/gui/GenCertDialog.h +++ b/retroshare-gui/src/gui/GenCertDialog.h @@ -44,7 +44,7 @@ private slots: void switchReuseExistingNode(); void grabMouse(); void updateCheckLabels(); - void useBobChecked(bool checked); + void useI2pChecked(bool checked); private: void initKeyList(); diff --git a/retroshare-gui/src/gui/GenCertDialog.ui b/retroshare-gui/src/gui/GenCertDialog.ui index 96f09cae2..482150beb 100644 --- a/retroshare-gui/src/gui/GenCertDialog.ui +++ b/retroshare-gui/src/gui/GenCertDialog.ui @@ -496,7 +496,7 @@ - Use BOB + Use I2P @@ -764,8 +764,8 @@ importIdentity_PB - + diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index 62928f701..ee71a9b44 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -184,10 +184,6 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) QObject::connect(ui.sbBobVarianceIn, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); QObject::connect(ui.sbBobVarianceOut, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); - // These two spin boxes are used for the same thing - keep them in sync! - QObject::connect(ui.hiddenpage_proxyPort_i2p, SIGNAL(valueChanged(int)), this, SLOT(syncI2PProxyPortNormal(int))); - QObject::connect(ui.hiddenpage_proxyPort_i2p_2, SIGNAL(valueChanged(int)), this, SLOT(syncI2PProxyPortSam(int))); - // These two line edits are used for the same thing - keep them in sync! QObject::connect(ui.hiddenpage_proxyAddress_i2p, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrNormal(QString))); QObject::connect(ui.hiddenpage_proxyAddress_i2p_2, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrSam(QString))); @@ -1528,20 +1524,6 @@ void ServerPage::toggleSamAdvancedSettings(bool checked) } } -void ServerPage::syncI2PProxyPortNormal(int i) -{ - ui.hiddenpage_proxyPort_i2p_2->setValue(i); -} - -void ServerPage::syncI2PProxyPortSam(int i) -{ - ui.hiddenpage_proxyPort_i2p->setValue(i); - - // update port, not necessary for same, we just want to keep it consistent - saveSam(); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::reloadConfig); -} - void ServerPage::syncI2PProxyAddrNormal(QString t) { ui.hiddenpage_proxyAddress_i2p_2->setText(t); @@ -1639,7 +1621,6 @@ void ServerPage::loadCommon() whileBlocking(ui.hiddenpage_proxyAddress_i2p) -> setText(QString::fromStdString(proxyaddr)); whileBlocking(ui.hiddenpage_proxyAddress_i2p_2)->setText(QString::fromStdString(proxyaddr)); // this one is for sam tab whileBlocking(ui.hiddenpage_proxyPort_i2p) -> setValue(proxyport); - whileBlocking(ui.hiddenpage_proxyPort_i2p_2)->setValue(proxyport); // this one is for sam tab // don't use whileBlocking here ui.cb_enableBob->setChecked(mSamSettings.enable); @@ -1727,7 +1708,7 @@ void ServerPage::updateStatusSam() QString bobSimpleText = QString(); bobSimpleText.append(tr("RetroShare uses SAMv3 to set up a %1 tunnel at %2:%3\n(id: %4)\n\n" - "When changing options (e.g. port) use the buttons at the bottom to restart SAMv3.\n\n"). + "When changing options use the buttons at the bottom to restart SAMv3.\n\n"). arg(mSamSettings.address.privateKey.empty() ? tr("client") : tr("server"), ui.hiddenpage_proxyAddress_i2p_2->text(), "7656", diff --git a/retroshare-gui/src/gui/settings/ServerPage.h b/retroshare-gui/src/gui/settings/ServerPage.h index a4bacfa40..7d82f2190 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.h +++ b/retroshare-gui/src/gui/settings/ServerPage.h @@ -108,9 +108,6 @@ private slots: void toggleSamAdvancedSettings(bool checked); - void syncI2PProxyPortNormal(int i); - void syncI2PProxyPortSam(int i); - void syncI2PProxyAddrNormal(QString); void syncI2PProxyAddrSam(QString); diff --git a/retroshare-gui/src/gui/settings/ServerPage.ui b/retroshare-gui/src/gui/settings/ServerPage.ui index 9d506bf38..6fad339c1 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.ui +++ b/retroshare-gui/src/gui/settings/ServerPage.ui @@ -982,27 +982,17 @@ If you have issues connecting over Tor check the Tor logs too. - - - I2P proxy port + + + Qt::Horizontal - - - - - - false + + + 40 + 20 + - - Not required for SAMv3 - - - 10 - - - 65535 - - + From 25cb152a7e6200915107ca234036bbae95a591cc Mon Sep 17 00:00:00 2001 From: sehraf Date: Sat, 7 Nov 2020 10:20:17 +0100 Subject: [PATCH 06/20] make pqissl notice bad file descriptor --- libretroshare/src/pqi/pqissl.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libretroshare/src/pqi/pqissl.cc b/libretroshare/src/pqi/pqissl.cc index 8ba82a105..1cfff8fbd 100644 --- a/libretroshare/src/pqi/pqissl.cc +++ b/libretroshare/src/pqi/pqissl.cc @@ -1742,7 +1742,13 @@ bool pqissl::moretoread(uint32_t usec) { rslog(RSL_ALERT, pqisslzone, "pqissl::moretoread() Select ERROR!"); - RS_WARN(errno); + RS_WARN(strerror(errno)); + + if (errno == EBADF) { + // happens when SAM is shut down + rslog(RSL_ALERT, pqisslzone, "pqissl::moretoread() -> calling reset()"); + reset_locked(); + } return 0; } From 47a4b726a3f218cfcbed9af85886169c63718d60 Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 19 Nov 2020 17:12:15 +0100 Subject: [PATCH 07/20] update libsam3 --- supportlibs/libsam3/Makefile | 6 +- supportlibs/libsam3/README.md | 36 ++++++- supportlibs/libsam3/examples/sam3/streamcs.c | 87 ++++++++++++++++ supportlibs/libsam3/examples/sam3/streamss.c | 72 +++++++++++++ supportlibs/libsam3/src/libsam3/libsam3.c | 101 ++++++++++++------- supportlibs/libsam3/src/libsam3/libsam3.h | 34 +++++++ supportlibs/libsam3/src/libsam3a/libsam3a.c | 18 +++- supportlibs/libsam3/src/libsam3a/libsam3a.h | 11 ++ 8 files changed, 323 insertions(+), 42 deletions(-) create mode 100644 supportlibs/libsam3/examples/sam3/streamcs.c create mode 100644 supportlibs/libsam3/examples/sam3/streamss.c diff --git a/supportlibs/libsam3/Makefile b/supportlibs/libsam3/Makefile index fc6f52245..f8d92a090 100644 --- a/supportlibs/libsam3/Makefile +++ b/supportlibs/libsam3/Makefile @@ -16,7 +16,6 @@ OBJS := ${LIB_OBJS} ${TEST_OBJS} LIB := libsam3.a - all: build check check: libsam3-tests @@ -34,8 +33,11 @@ clean: rm -f libsam3-tests ${LIB} ${OBJS} examples/sam3/samtest %.o: %.c Makefile - ${CC} ${CFLAGS} -c $< -o $@ + ${CC} ${CFLAGS} $(LDFLAGS) -c $< -o $@ fmt: find . -name '*.c' -exec clang-format -i {} \; find . -name '*.h' -exec clang-format -i {} \; + +info: + @echo $(AR) diff --git a/supportlibs/libsam3/README.md b/supportlibs/libsam3/README.md index 98dfe4205..cd6b291a5 100644 --- a/supportlibs/libsam3/README.md +++ b/supportlibs/libsam3/README.md @@ -6,7 +6,7 @@ A C library for the [SAM v3 API](https://geti2p.net/en/docs/api/samv3). ## Development Status -Unmaintained, but PRs welcome! +Maintained by idk, PRs are accepted on [I2P gitlab](https://i2pgit.org/i2p-hackers/libsam3)/[I2P gitlab](http://git.idk.i2p/i2p-hackers/libsam3), and on github at the official mirror repository: [i2p/libsam3](https://github.com/i2p/libsam3). ## Usage @@ -16,3 +16,37 @@ Copy the two files from one of the following locations into your codebase: - `src/libsam3a` - Asynchronous implementation. See `examples/` for how to use various parts of the API. + +## Cross-Compiling for Windows from debian: + +Set your cross-compiler up: + +``` sh +export CC=x86_64-w64-mingw32-gcc +export CFLAGS='-Wall -O2 ' +export LDFLAGS='-lmingw32 -lws2_32 -lwsock32 -mwindows' +``` + +and run `make build`. Only libsam3 is available for Windows, libsam3a will be +made available at a later date. +` + +## Linker(Windows) + +When building for Windows remember to set the flags to link to the Winsock and Windows +libraries. + +`-lmingw32 -lws2_32 -lwsock32 -mwindows` + +This may apply when cross-compiling or compiling from Windows with mingw. + +## Cool Projects using libsam3 + +Are you using libsam3 to provide an a cool I2P based feature to your project? Let us know about it(and how +it uses libsam3) and we'll think about adding it here*! + + 1. [Retroshare](https://retroshare.cc) + +*Projects which are listed here must be actively maintained. Those which intentionally violate +the law or the rights of a person or persons directly won't be considered. Neither will obvious +trolling. The maintainer will make the final decision. diff --git a/supportlibs/libsam3/examples/sam3/streamcs.c b/supportlibs/libsam3/examples/sam3/streamcs.c new file mode 100644 index 000000000..25bd22072 --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/streamcs.c @@ -0,0 +1,87 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +#define KEYFILE "streams.key" + +int main(int argc, char *argv[]) { + Sam3Session ses; + Sam3Connection *conn; + char cmd[1024], destkey[617]; // 616 chars + \0 + // + libsam3_debug = 1; + // + memset(destkey, 0, sizeof(destkey)); + // + if (argc < 2) { + FILE *fl = fopen(KEYFILE, "rb"); + // + if (fl != NULL) { + if (fread(destkey, 616, 1, fl) == 1) { + fclose(fl); + goto ok; + } + fclose(fl); + } + printf("usage: streamc PUBKEY\n"); + return 1; + } else { + if (!sam3CheckValidKeyLength(argv[1])) { + fprintf(stderr, "FATAL: invalid key length! %s %lu\n", argv[1], + strlen(argv[1])); + return 1; + } + strcpy(destkey, argv[1]); + } + // +ok: + printf("creating session...\n"); + // create TRANSIENT session + if (sam3CreateSilentSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, + SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, + 4, NULL) < 0) { + fprintf(stderr, "FATAL: can't create session\n"); + return 1; + } + // + printf("connecting...\n"); + if ((conn = sam3StreamConnect(&ses, destkey)) == NULL) { + fprintf(stderr, "FATAL: can't connect: %s\n", ses.error); + sam3CloseSession(&ses); + return 1; + } + // + // now waiting for incoming connection + printf("sending test command...\n"); + if (sam3tcpPrintf(conn->fd, "test\n") < 0) + goto error; + if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) + goto error; + printf("echo: %s\n", cmd); + // + printf("sending quit command...\n"); + if (sam3tcpPrintf(conn->fd, "quit\n") < 0) + goto error; + // + sam3CloseConnection(conn); + sam3CloseSession(&ses); + return 0; +error: + fprintf(stderr, "FATAL: some error occured!\n"); + sam3CloseConnection(conn); + sam3CloseSession(&ses); + return 1; +} diff --git a/supportlibs/libsam3/examples/sam3/streamss.c b/supportlibs/libsam3/examples/sam3/streamss.c new file mode 100644 index 000000000..ffbc974da --- /dev/null +++ b/supportlibs/libsam3/examples/sam3/streamss.c @@ -0,0 +1,72 @@ +/* This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + * + * I2P-Bote: + * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV + * we are the Borg. */ +#include +#include +#include +#include +#include + +#include "../libsam3/libsam3.h" + +#define KEYFILE "streams.key" + +int main(int argc, char *argv[]) { + Sam3Session ses; + Sam3Connection *conn; + FILE *fl; + // + libsam3_debug = 1; + // + printf("creating session...\n"); + // create TRANSIENT session + if (sam3CreateSilentSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, + SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, + 4, NULL) < 0) { + fprintf(stderr, "FATAL: can't create session\n"); + return 1; + } + // + printf("PUB KEY\n=======\n%s\n=======\n", ses.pubkey); + if ((fl = fopen(KEYFILE, "wb")) != NULL) { + fwrite(ses.pubkey, strlen(ses.pubkey), 1, fl); + fclose(fl); + } + // + printf("starting stream acceptor...\n"); + if ((conn = sam3StreamAccept(&ses)) == NULL) { + fprintf(stderr, "FATAL: can't accept: %s\n", ses.error); + sam3CloseSession(&ses); + return 1; + } + printf("FROM\n====\n%s\n====\n", conn->destkey); + // + printf("starting main loop...\n"); + for (;;) { + char cmd[256]; + // + if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) + goto error; + printf("cmd: [%s]\n", cmd); + if (strcmp(cmd, "quit") == 0) + break; + // echo command + if (sam3tcpPrintf(conn->fd, "re: %s\n", cmd) < 0) + goto error; + } + // + sam3CloseSession(&ses); + unlink(KEYFILE); + return 0; +error: + fprintf(stderr, "FATAL: some error occured!\n"); + sam3CloseSession(&ses); + unlink(KEYFILE); + return 1; +} diff --git a/supportlibs/libsam3/src/libsam3/libsam3.c b/supportlibs/libsam3/src/libsam3/libsam3.c index 476372a0b..5a645806f 100644 --- a/supportlibs/libsam3/src/libsam3/libsam3.c +++ b/supportlibs/libsam3/src/libsam3/libsam3.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -20,16 +19,27 @@ #include #include +#ifdef __MINGW32__ +//#include +#include +#include +#include +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif +#ifndef SHUT_RDWR +#define SHUT_RDWR 2 +#endif +#endif + +#ifdef __unix__ #include +#include #include #include #include #include - -#ifdef WINDOWS_SYS -#include -#endif // WINDOWS_SYS - +#endif //////////////////////////////////////////////////////////////////////////////// int libsam3_debug = 0; @@ -104,11 +114,6 @@ int sam3tcpConnectIP(uint32_t ip, int port) { } } // - // Set this for all outgoing SAM connections. Most SAM commands should be answered rather fast except CREATE SESSION maybe. - // This should be enough to let SAM establish a session. - sam3tcpSetTimeoutSend(fd, 5 * 60 * 1000); - sam3tcpSetTimeoutReceive(fd, 5 * 60 * 1000); - // setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); // if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { @@ -161,13 +166,8 @@ int sam3tcpConnect(const char *hostname, int port, uint32_t *ip) { // <0: error; 0: ok int sam3tcpDisconnect(int fd) { if (fd >= 0) { -#ifndef WINDOWS_SYS // ie UNIX - shutdown(fd, SHUT_RDWR); - return close(fd); -#else - return closesocket(fd); -#endif - + shutdown(fd, SHUT_RDWR); + return close(fd); } // return -1; @@ -823,6 +823,18 @@ int sam3CloseSession(Sam3Session *ses) { return -1; } +int sam3CreateSilentSession(Sam3Session *ses, const char *hostname, int port, + const char *privkey, Sam3SessionType type, + Sam3SigType sigType, const char *params) { + int r = + sam3CreateSession(ses, hostname, port, privkey, type, sigType, params); + if (r != 0) { + return r; + } + ses->silent = true; + return 0; +} + int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, const char *privkey, Sam3SessionType type, Sam3SigType sigType, const char *params) { @@ -840,6 +852,7 @@ int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, memset(ses, 0, sizeof(Sam3Session)); ses->fd = -1; ses->fwd_fd = -1; + ses->silent = false; // if (privkey != NULL && strlen(privkey) < SAM3_PRIVKEY_MIN_SIZE) goto error; @@ -936,8 +949,9 @@ Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey) { strcpyerr(ses, "IO_ERROR_SK"); goto error; } - if (sam3tcpPrintf(conn->fd, "STREAM CONNECT ID=%s DESTINATION=%s\n", - ses->channel, destkey) < 0) { + if (sam3tcpPrintf(conn->fd, + "STREAM CONNECT ID=%s DESTINATION=%s SILENT=%s\n", + ses->channel, destkey, checkIsSilent(ses)) < 0) { strcpyerr(ses, "IO_ERROR"); goto error; } @@ -945,16 +959,18 @@ Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey) { strcpyerr(ses, "IO_ERROR"); goto error; } - if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { - const char *v = sam3FindField(rep, "RESULT"); - // - strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR")); - sam3CloseConnectionInternal(conn); - free(conn); - conn = NULL; - } else { - // no error - strcpyerr(ses, NULL); + if (!ses->silent) { + if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { + const char *v = sam3FindField(rep, "RESULT"); + // + strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR")); + sam3CloseConnectionInternal(conn); + free(conn); + conn = NULL; + } else { + // no error + strcpyerr(ses, NULL); + } } sam3FreeFieldList(rep); if (conn != NULL) { @@ -1002,11 +1018,13 @@ Sam3Connection *sam3StreamAccept(Sam3Session *ses) { strcpyerr(ses, "IO_ERROR_RP"); goto error; } - if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { - const char *v = sam3FindField(rep, "RESULT"); - // - strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES")); - goto error; + if (!ses->silent) { + if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { + const char *v = sam3FindField(rep, "RESULT"); + // + strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES")); + goto error; + } } if (sam3tcpReceiveStr(conn->fd, repstr, sizeof(repstr)) < 0) { strcpyerr(ses, "IO_ERROR_RP1"); @@ -1040,6 +1058,14 @@ Sam3Connection *sam3StreamAccept(Sam3Session *ses) { return NULL; } +const char *checkIsSilent(Sam3Session *ses) { + if (ses->silent == true) { + return "true"; + } else { + return "false"; + } +} + int sam3StreamForward(Sam3Session *ses, const char *hostname, int port) { if (ses != NULL) { SAMFieldList *rep = NULL; @@ -1060,8 +1086,9 @@ int sam3StreamForward(Sam3Session *ses, const char *hostname, int port) { strcpyerr(ses, "IO_ERROR_SK"); goto error; } - if (sam3tcpPrintf(ses->fwd_fd, "STREAM FORWARD ID=%s PORT=%d HOST=%s SILENT=true\n", - ses->channel, port, hostname) < 0) { + if (sam3tcpPrintf(ses->fwd_fd, + "STREAM FORWARD ID=%s PORT=%d HOST=%s SILENT=%s\n", + ses->channel, port, hostname, checkIsSilent(ses)) < 0) { strcpyerr(ses, "IO_ERROR_PF"); goto error; } diff --git a/supportlibs/libsam3/src/libsam3/libsam3.h b/supportlibs/libsam3/src/libsam3/libsam3.h index 3eb261d89..16942f594 100644 --- a/supportlibs/libsam3/src/libsam3/libsam3.h +++ b/supportlibs/libsam3/src/libsam3/libsam3.h @@ -10,9 +10,19 @@ #ifndef LIBSAM3_H #define LIBSAM3_H +#include #include #include +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#undef ssize_t +#ifdef _WIN64 +typedef signed int64 ssize_t; +typedef int ssize_t; +#endif /* _WIN64 */ +#endif /* _SSIZE_T_DEFINED */ + #ifdef __cplusplus extern "C" { #endif @@ -138,6 +148,7 @@ typedef struct Sam3Session { int port; // this will be changed to UDP port for DRAM/RAW (can be 0) struct Sam3Connection *connlist; // list of opened connections int fwd_fd; + bool silent; } Sam3Session; typedef struct Sam3Connection { @@ -166,6 +177,22 @@ extern int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, const char *privkey, Sam3SessionType type, Sam3SigType sigType, const char *params); +/* + * create SAM session with SILENT=True + * pass NULL as hostname for 'localhost' and 0 as port for 7656 + * pass NULL as privkey to create TRANSIENT session + * 'params' can be NULL + * see http://www.i2p2.i2p/i2cp.html#options for common options, + * and http://www.i2p2.i2p/streaming.html#options for STREAM options + * if result<0: error, 'ses' fields are undefined, no need to call + * sam3CloseSession() if result==0: ok, all 'ses' fields are filled + * TODO: don't clear 'error' field on error (and set it to something meaningful) + */ +extern int sam3CreateSilentSession(Sam3Session *ses, const char *hostname, + int port, const char *privkey, + Sam3SessionType type, Sam3SigType sigType, + const char *params); + /* * close SAM session (and all it's connections) * returns <0 on error, 0 on ok @@ -173,6 +200,13 @@ extern int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, */ extern int sam3CloseSession(Sam3Session *ses); +/* + * check to see if a SAM session is silent and output + * characters for use with sam3tcpPrintf() checkIsSilent + */ + +const char *checkIsSilent(Sam3Session *ses); + /* * Check to make sure that the destination in use is of a valid length, returns * 1 if true and 0 if false. diff --git a/supportlibs/libsam3/src/libsam3a/libsam3a.c b/supportlibs/libsam3/src/libsam3a/libsam3a.c index 40921e6ba..e5983b008 100644 --- a/supportlibs/libsam3/src/libsam3a/libsam3a.c +++ b/supportlibs/libsam3/src/libsam3a/libsam3a.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -22,13 +21,28 @@ #include #include +#ifdef __MINGW32__ +//#include +#include +#include +#include +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif +#ifndef SHUT_RDWR +#define SHUT_RDWR 2 +#endif +#endif + +#ifdef __unix__ #include +#include #include #include #include #include #include - +#endif //////////////////////////////////////////////////////////////////////////////// int libsam3a_debug = 0; diff --git a/supportlibs/libsam3/src/libsam3a/libsam3a.h b/supportlibs/libsam3/src/libsam3a/libsam3a.h index 4ddec7952..dfd4c0cba 100644 --- a/supportlibs/libsam3/src/libsam3a/libsam3a.h +++ b/supportlibs/libsam3/src/libsam3a/libsam3a.h @@ -18,6 +18,17 @@ #include +#ifdef __MINGW32__ +//#include +#include +#include +#include +//#define SOCK_CLOEXEC O_CLOEXEC +//#define SOCK_NONBLOCK O_NONBLOCK +#define SOCK_CLOEXEC 02000000 +#define SOCK_NONBLOCK FIONBIO +#endif + #ifdef __cplusplus extern "C" { #endif From c869b9757f3ea0de4db955a977d13324728ba546 Mon Sep 17 00:00:00 2001 From: sehraf Date: Sat, 21 Nov 2020 15:00:40 +0100 Subject: [PATCH 08/20] drop BOB code --- libretroshare/src/libretroshare.pro | 5 +- libretroshare/src/pqi/pqissli2pbob.cpp | 52 - libretroshare/src/pqi/pqissli2pbob.h | 52 - libretroshare/src/rsserver/rsinit.cc | 1 - .../src/services/autoproxy/p3i2pbob.cc | 1117 ----------------- .../src/services/autoproxy/p3i2pbob.h | 256 ---- 6 files changed, 1 insertion(+), 1482 deletions(-) delete mode 100644 libretroshare/src/pqi/pqissli2pbob.cpp delete mode 100644 libretroshare/src/pqi/pqissli2pbob.h delete mode 100644 libretroshare/src/services/autoproxy/p3i2pbob.cc delete mode 100644 libretroshare/src/services/autoproxy/p3i2pbob.h diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index c8f9c4b95..0a8ba44a7 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -394,7 +394,6 @@ HEADERS += pqi/authssl.h \ pqi/pqissl.h \ pqi/pqissllistener.h \ pqi/pqisslpersongrp.h \ - pqi/pqissli2pbob.h \ pqi/pqisslproxy.h \ pqi/pqistore.h \ pqi/pqistreamer.h \ @@ -456,7 +455,7 @@ HEADERS += rsitems/rsitem.h \ rsitems/rsgxsupdateitems.h \ rsitems/rsserviceinfoitems.h \ -HEADERS += services/autoproxy/p3i2pbob.h \ +HEADERS += \ services/rseventsservice.h \ services/autoproxy/rsautoproxymonitor.h \ services/p3msgservice.h \ @@ -560,7 +559,6 @@ SOURCES += pqi/authgpg.cc \ pqi/pqissl.cc \ pqi/pqissllistener.cc \ pqi/pqisslpersongrp.cc \ - pqi/pqissli2pbob.cpp \ pqi/pqisslproxy.cc \ pqi/pqistore.cc \ pqi/pqistreamer.cc \ @@ -618,7 +616,6 @@ SOURCES += serialiser/rsbaseserial.cc \ SOURCES += services/autoproxy/rsautoproxymonitor.cc \ services/rseventsservice.cc \ - services/autoproxy/p3i2pbob.cc \ services/p3msgservice.cc \ services/p3service.cc \ services/p3statusservice.cc \ diff --git a/libretroshare/src/pqi/pqissli2pbob.cpp b/libretroshare/src/pqi/pqissli2pbob.cpp deleted file mode 100644 index 5551ce788..000000000 --- a/libretroshare/src/pqi/pqissli2pbob.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * libretroshare/src/pqi: pqissli2pbob.cc * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#include "pqissli2pbob.h" - -bool pqissli2pbob::connect_parameter(uint32_t type, const std::string &value) -{ - if (type == NET_PARAM_CONNECT_DOMAIN_ADDRESS) - { - RS_STACK_MUTEX(mSslMtx); - // a new line must be appended! - mI2pAddr = value + '\n'; - return true; - } - - return pqissl::connect_parameter(type, value); -} - -int pqissli2pbob::Basic_Connection_Complete() -{ - int ret; - - if ((ret = pqissl::Basic_Connection_Complete()) != 1) - { - // basic connection not complete. - return ret; - } - - // send addr. (new line is already appended) - ret = send(sockfd, mI2pAddr.c_str(), mI2pAddr.length(), 0); - if (ret != (int)mI2pAddr.length()) - return -1; - return 1; -} diff --git a/libretroshare/src/pqi/pqissli2pbob.h b/libretroshare/src/pqi/pqissli2pbob.h deleted file mode 100644 index b1a643a75..000000000 --- a/libretroshare/src/pqi/pqissli2pbob.h +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * libretroshare/src/pqi: pqissli2pbob.h * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#ifndef PQISSLI2PBOB_H -#define PQISSLI2PBOB_H - -#include "pqi/pqissl.h" - -/* - * This class is a minimal varied version of pqissl to work with I2P BOB tunnels. - * The only difference is that the [.b32].i2p addresses must be sent first. - * - * Everything else is untouched. - */ - -class pqissli2pbob : public pqissl -{ -public: - pqissli2pbob(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm) - : pqissl(l, parent, lm) {} - - // NetInterface interface -public: - bool connect_parameter(uint32_t type, const std::string &value); - - // pqissl interface -protected: - int Basic_Connection_Complete(); - -private: - std::string mI2pAddr; -}; - -#endif // PQISSLI2PBOB_H diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 864099f01..ad39f46a1 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -717,7 +717,6 @@ RsGRouter *rsGRouter = NULL ; # include "rs_upnp/upnphandler_miniupnp.h" #endif // def RS_USE_LIBUPNP -#include "services/autoproxy/p3i2pbob.h" #include "services/autoproxy/p3i2psam3.h" #include "services/autoproxy/rsautoproxymonitor.h" diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc deleted file mode 100644 index f9eb3d9b3..000000000 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ /dev/null @@ -1,1117 +0,0 @@ -/******************************************************************************* - * libretroshare/src/services/autoproxy: p3i2pbob.cc * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#include -#include /* for usleep() */ - -#include "p3i2pbob.h" - -#include "pqi/p3peermgr.h" -#include "rsitems/rsconfigitems.h" -#include "util/radix32.h" -#include "util/radix64.h" -#include "util/rsdebug.h" -#include "util/rstime.h" -#include "util/rsprint.h" -#include "util/rsrandom.h" - -static const std::string kConfigKeyBOBEnable = "BOB_ENABLE"; -static const std::string kConfigKeyBOBKey = "BOB_KEY"; -static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; -static const std::string kConfigKeyInLength = "IN_LENGTH"; -static const std::string kConfigKeyInQuantity = "IN_QUANTITY"; -static const std::string kConfigKeyInVariance = "IN_VARIANCE"; -static const std::string kConfigKeyOutLength = "OUT_LENGTH"; -static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY"; -static const std::string kConfigKeyOutVariance = "OUT_VARIANCE"; - -/// Sleep duration for receiving loop in error/no-data case -static const useconds_t sleepTimeRecv = 250; // times 1000 = 250ms -/// Sleep duration for everything else -static const useconds_t sleepTimeWait = 50; // times 1000 = 50ms or 0.05s -static const int sleepFactorDefault = 10; // 0.5s -static const int sleepFactorFast = 1; // 0.05s -static const int sleepFactorSlow = 20; // 1s - -static const rstime_t selfCheckPeroid = 30; - -void doSleep(useconds_t timeToSleepMS) { - rstime::rs_usleep((useconds_t) (timeToSleepMS * 1000)); -} - -p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr) - : RsTickingThread(), p3Config(), - mState(csIdel), mTask(ctIdle), - mStateOld(csIdel), mTaskOld(ctIdle), - mBOBState(bsCleared), mPeerMgr(peerMgr), - mConfigLoaded(false), mSocket(0), - mLastProxyCheck(time(NULL)), - mProcessing(NULL), mLock("I2P-BOB") -{ - // set defaults - mSetting.initDefault(); - - mCommands.clear(); -} - -bool p3I2pBob::isEnabled() -{ - RS_STACK_MUTEX(mLock); - return mSetting.enable; -} - -bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) -{ - RS_DBG(""); - - // update config - { - RS_STACK_MUTEX(mLock); - if (!mConfigLoaded) { - finalizeSettings_locked(); - mConfigLoaded = true; - } else { - updateSettings_locked(); - } - } - - RS_DBG("config updated"); - - // request keys - // p3I2pBob::stateMachineBOB expects mProcessing to be set therefore - // we create this fake ticket without a callback or data - // ticket gets deleted later by this service - taskTicket *fakeTicket = rsAutoProxyMonitor::getTicket(); - fakeTicket->task = autoProxyTask::receiveKey; - processTaskAsync(fakeTicket); - - RS_DBG("fakeTicket requested"); - - // now start thread - start("I2P-BOB gen key"); - - RS_DBG("thread started"); - - int counter = 0; - // wait for keys - for(;;) { - doSleep(sleepTimeWait * sleepFactorDefault); - - RS_STACK_MUTEX(mLock); - - // wait for tast change - if (mTask != ctRunGetKeys) - break; - - if (++counter > 30) { - RS_DBG4("timeout!"); - return false; - } - } - - RS_DBG("got keys"); - - // stop thread - fullstop(); - - RS_DBG("thread stopped"); - - { - RS_STACK_MUTEX(mLock); - addr = mSetting.address.base32; - } - - RS_DBG4("addr ", addr); - - return true; -} - -void p3I2pBob::processTaskAsync(taskTicket *ticket) -{ - switch (ticket->task) { - case autoProxyTask::start: - case autoProxyTask::stop: - case autoProxyTask::receiveKey: - case autoProxyTask::proxyStatusCheck: - { - RS_STACK_MUTEX(mLock); - mPending.push(ticket); - } - break; - default: - RS_DBG("unknown task"); - rsAutoProxyMonitor::taskError(ticket); - break; - } -} - -void p3I2pBob::processTaskSync(taskTicket *ticket) -{ - bool data = !!ticket->data; - - // check wether we can process the task immediately or have to queue it - switch (ticket->task) { - case autoProxyTask::status: - // check if everything needed is set - if (!data) { - RS_DBG("autoProxyTask::status data is missing"); - rsAutoProxyMonitor::taskError(ticket); - break; - } - - // get states - getStates((struct bobStates*)ticket->data); - - // finish task - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::getSettings: - // check if everything needed is set - if (!data) { - RS_DBG("autoProxyTask::getSettings data is missing"); - rsAutoProxyMonitor::taskError(ticket); - break; - } - - // get settings - getBOBSettings((struct bobSettings *)ticket->data); - - // finish task - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::setSettings: - // check if everything needed is set - if (!data) { - RS_DBG("autoProxyTask::setSettings data is missing"); - rsAutoProxyMonitor::taskError(ticket); - break; - } - - // set settings - setBOBSettings((struct bobSettings *)ticket->data); - - // finish task - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::reloadConfig: - { - RS_STACK_MUTEX(mLock); - updateSettings_locked(); - } - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::getErrorInfo: - if (!data) { - RS_DBG("autoProxyTask::getErrorInfo data is missing"); - rsAutoProxyMonitor::taskError(ticket); - } else { - RS_STACK_MUTEX(mLock); - *(std::string *)ticket->data = mErrorMsg; - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - } - break; - default: - RS_DBG("unknown task"); - rsAutoProxyMonitor::taskError(ticket); - break; - } -} - -bool inline isAnswerOk(const std::string &answer) { - return (answer.compare(0, 2, "OK") == 0); -} - -bool inline isTunnelActiveError(const std::string &answer) { - return answer.compare(0, 22, "ERROR tunnel is active") == 0; -} - -void p3I2pBob::threadTick() -{ - int sleepTime = 0; - { - RS_STACK_MUTEX(mLock); - RS_DBG4("data_tick mState: ", mState, " mTask: ", mTask, " mBOBState: ", mBOBState, " mPending: ", mPending.size()); - } - - sleepTime += stateMachineController(); - sleepTime += stateMachineBOB(); - - sleepTime >>= 1; - - // sleep outisde of lock! - doSleep(sleepTime * sleepTimeWait); -} - -int p3I2pBob::stateMachineBOB() -{ - std::string answer; - bobStateInfo currentState; - - { - RS_STACK_MUTEX(mLock); - if (mBOBState == bsCleared || !mConfigLoaded) { - // we don't have work to do - sleep longer - return sleepFactorSlow; - } - - // get next command - currentState = mCommands[mBOBState]; - } - - // this call can take a while - // do NOT hold the lock - answer = executeCommand(currentState.command); - - // can hold the lock for the rest of the function - RS_STACK_MUTEX(mLock); - - // special state first - if (mBOBState == bsList) { - int counter = 0; - while (answer.find("OK Listing done") == std::string::npos) { - RS_DBG3("stateMachineBOB status check: read loop, counter: ", counter); - answer += recv(); - counter++; - } - - if (answer.find(mTunnelName) == std::string::npos) { - RS_DBG("status check: tunnel down!"); - // signal error - *((bool *)mProcessing->data) = true; - } - - mBOBState = currentState.nextState; - } else if (isAnswerOk(answer)) { - // check for other special states - std::string key; - switch (mBOBState) { - case bsNewkeysN: - key = answer.substr(3, answer.length()-3); - mSetting.address.base32 = i2p::keyToBase32Addr(key); - IndicateConfigChanged(); - break; - case bsGetkeys: - key = answer.substr(3, answer.length()-3); - mSetting.address.privateKey = key; - IndicateConfigChanged(); - break; - default: - break; - } - - // goto next command - mBOBState = currentState.nextState; - } else { - return stateMachineBOB_locked_failure(answer, currentState); - } - return sleepFactorFast; -} - -int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bobStateInfo ¤tState) -{ - // wait in case of active tunnel - // happens when trying to clear a stopping tunnel - if (isTunnelActiveError(answer)) { - return sleepFactorDefault; - } - - RS_DBG("FAILED to run command: ", currentState.command); - RS_DBG("answer: ", answer); - - mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n'); - mErrorMsg.append("reason '" + answer + "'" + '\n'); - - // this error handling needs testing! - mStateOld = mState; - mState = csError; - switch (mBOBState) { - case bsGetnick: - // failed getting nick - // tunnel is probably non existing - case bsClear: - // tunnel is cleared - mBOBState = bsQuit; - break; - case bsStop: - // failed stopping - // tunnel us probably not running - // continue to clearing - mBOBState = bsClear; - break; - case bsQuit: - // this can happen when the - // connection is somehow broken - // just try to disconnect - disconnectI2P(); - mBOBState = bsCleared; - break; - default: - // try to recover - mBOBState = bsGetnick; - break; - } - - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController() -{ - RS_STACK_MUTEX(mLock); - - switch (mState) { - case csIdel: - return stateMachineController_locked_idle(); - case csDoConnect: - if (!connectI2P()) { - RS_DBG("doConnect: unable to connect"); - mStateOld = mState; - mState = csError; - mErrorMsg = "unable to connect to BOB port"; - return sleepFactorSlow; - } - - RS_DBG4("doConnect: connected"); - mState = csConnected; - break; - case csConnected: - return stateMachineController_locked_connected(); - case csWaitForBob: - // check connection problems - if (mSocket == 0) { - RS_DBG("waitForBob: conection lost"); - mStateOld = mState; - mState = csError; - mErrorMsg = "connection lost to BOB"; - return sleepFactorDefault; - } - - // check for finished BOB protocol - if (mBOBState == bsCleared) { - // done - RS_DBG4("waitForBob: mBOBState == bsCleared"); - mState = csDoDisconnect; - } - break; - case csDoDisconnect: - if (!disconnectI2P() || mSocket != 0) { - // just in case - RS_DBG("doDisconnect: can't disconnect"); - mStateOld = mState; - mState = csError; - mErrorMsg = "unable to disconnect from BOB"; - return sleepFactorDefault; - } - - RS_DBG4("doDisconnect: disconnected"); - mState = csDisconnected; - break; - case csDisconnected: - return stateMachineController_locked_disconnected(); - case csError: - return stateMachineController_locked_error(); - } - - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController_locked_idle() -{ - // do some sanity checks - // use asserts becasue these things indicate wrong/broken state machines that need to be fixed ASAP! - assert(mBOBState == bsCleared); - assert(mSocket == 0); - assert(mState == csIdel || mState == csDisconnected); - - controllerTask oldTask = mTask; - // check for new task - if (mProcessing == NULL && !mPending.empty()) { - mProcessing = mPending.front(); - mPending.pop(); - - if (!mSetting.enable && ( - mProcessing->task == autoProxyTask::start || - mProcessing->task == autoProxyTask::stop || - mProcessing->task == autoProxyTask::proxyStatusCheck)) { - // skip since we are not enabled - RS_DBG1("disabled -> skipping ticket"); - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled); - mProcessing = NULL; - } else { - // set states - switch (mProcessing->task) { - case autoProxyTask::start: - mLastProxyCheck = time(NULL); - mTask = ctRunSetUp; - break; - case autoProxyTask::stop: - mTask = ctRunShutDown; - break; - case autoProxyTask::receiveKey: - mTaskOld = mTask; - mTask = ctRunGetKeys; - break; - case autoProxyTask::proxyStatusCheck: - mTaskOld = mTask; - mTask = ctRunCheck; - break; - default: - RS_DBG1("unknown async task"); - rsAutoProxyMonitor::taskError(mProcessing); - mProcessing = NULL; - break; - } - } - - mErrorMsg.clear(); - } - - // periodically check - if (mTask == ctRunSetUp && mLastProxyCheck < time(NULL) - selfCheckPeroid) { - taskTicket *tt = rsAutoProxyMonitor::getTicket(); - tt->task = autoProxyTask::proxyStatusCheck; - tt->data = (void *) new bool; - - *((bool *)tt->data) = false; - - mPending.push(tt); - - mLastProxyCheck = time(NULL); - } - - // wait for new task - if (!!mProcessing) { - // check if task was changed - if (mTask != oldTask) { - mState = csDoConnect; - } else { - // A ticket shall be processed but the state didn't change. - // This means that what ever is requested in the ticket - // was requested before already. - // -> set mState to csDisconnected to answer the ticket - mState = csDisconnected; - } - return sleepFactorFast; - } - - return sleepFactorSlow; -} - -int p3I2pBob::stateMachineController_locked_connected() -{ - // set proper bob state - switch (mTask) { - case ctRunSetUp: - // when we have a key use it for server tunnel! - if(mSetting.address.privateKey.empty()) { - RS_DBG4("setting mBOBState = setnickC"); - mBOBState = bsSetnickC; - } else { - RS_DBG4("setting mBOBState = setnickS"); - mBOBState = bsSetnickS; - } - break; - case ctRunShutDown: - // shut down existing tunnel - RS_DBG4("setting mBOBState = getnick"); - mBOBState = bsGetnick; - break; - case ctRunCheck: - RS_DBG4("setting mBOBState = list"); - mBOBState = bsList; - break; - case ctRunGetKeys: - RS_DBG4("setting mBOBState = setnickN"); - mBOBState = bsSetnickN; - break; - case ctIdle: - RS_DBG("task is idle. This should not happen!"); - break; - } - - mState = csWaitForBob; - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController_locked_disconnected() -{ - // check if we had an error - bool errorHappened = (mStateOld == csError); - - if(errorHappened) { - // reset old state - mStateOld = csIdel; - RS_DBG("error during process!"); - } - - // answer ticket - controllerState newState = csIdel; - switch (mTask) { - case ctRunSetUp: - if (errorHappened) { - rsAutoProxyMonitor::taskError(mProcessing); - // switch to error - newState = csError; - } else { - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::online); - } - break; - case ctRunShutDown: - // don't care about error here - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::offline); - break; - case ctRunCheck: - // get result and delete dummy ticket - errorHappened |= *((bool *)mProcessing->data); - delete (bool *)mProcessing->data; - delete mProcessing; - - // restore old task - mTask = mTaskOld; - - if (!errorHappened) { - RS_DBG4("run check result: ok"); - break; - } - // switch to error - newState = csError; - RS_DBG("run check result: error"); - mErrorMsg = "Connection check failed. Will try to restart tunnel."; - - break; - case ctRunGetKeys: - if (!errorHappened) { - // rebuild commands - updateSettings_locked(); - - if (mProcessing->data) - *((struct bobSettings *)mProcessing->data) = mSetting; - - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::ok); - } else { - rsAutoProxyMonitor::taskError(mProcessing); - // switch to error - newState = csError; - } - - // restore old task - mTask = mTaskOld; - break; - case ctIdle: - RS_DBG("task is idle. This should not happen!"); - rsAutoProxyMonitor::taskError(mProcessing); - } - mProcessing = NULL; - mState = newState; - - if (newState == csError) - mLastProxyCheck = time(NULL); - - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController_locked_error() -{ - // wait for bob protocoll - if (mBOBState != bsCleared) { - RS_DBG4("waiting for BOB"); - return sleepFactorFast; - } - -#if 0 - RS_DBG4("stateMachineController_locked_error: mProcessing: ", (mProcessing ? "not null" : "null")); -#endif - - // try to finish ticket - if (mProcessing) { - switch (mTask) { - case ctRunCheck: - // connection check failed at some point - RS_DBG("failed to check proxy status (it's likely dead)!"); - *((bool *)mProcessing->data) = true; - mState = csDoDisconnect; - mStateOld = csIdel; - // keep the error message - break; - case ctRunShutDown: - // not a big deal though - RS_DBG("failed to shut down tunnel (it's likely dead though)!"); - mState = csDoDisconnect; - mStateOld = csIdel; - mErrorMsg.clear(); - break; - case ctIdle: - // should not happen but we need to deal with it - // this will produce some error messages in the log and finish the task (marked as failed) - RS_DBG("task is idle. This should not happen!"); - mState = csDoDisconnect; - mStateOld = csIdel; - mErrorMsg.clear(); - break; - case ctRunGetKeys: - case ctRunSetUp: - RS_DBG("failed to receive key / start up"); - mStateOld = csError; - mState = csDoDisconnect; - // keep the error message - break; - } - return sleepFactorFast; - } - - // periodically retry - if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) { - RS_DBG("retrying"); - - mLastProxyCheck = time(NULL); - mErrorMsg.clear(); - - // create fake ticket - taskTicket *tt = rsAutoProxyMonitor::getTicket(); - tt->task = autoProxyTask::start; - mPending.push(tt); - } - - // check for new tickets - if (!mPending.empty()) { - RS_DBG4("processing new ticket"); - - // reset and try new task - mTask = ctIdle; - mState = csIdel; - return sleepFactorFast; - } - - return sleepFactorDefault; -} - -RsSerialiser *p3I2pBob::setupSerialiser() -{ - RsSerialiser* rsSerialiser = new RsSerialiser(); - rsSerialiser->addSerialType(new RsGeneralConfigSerialiser()); - - return rsSerialiser; -} - -#define addKVS(_vitem, _kv, _key, _value) \ - _kv.key = _key;\ - _kv.value = _value;\ - _vitem->tlvkvs.pairs.push_back(_kv); - -#define addKVSInt(_vitem, _kv, _key, _value) \ - _kv.key = _key;\ - rs_sprintf(_kv.value, "%d", _value);\ - _vitem->tlvkvs.pairs.push_back(_kv); - -bool p3I2pBob::saveList(bool &cleanup, std::list &lst) -{ - RS_DBG4(""); - - cleanup = true; - RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; - RsTlvKeyValue kv; - - RS_STACK_MUTEX(mLock); - addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enable ? "TRUE" : "FALSE") - addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.address.privateKey) - addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.address.base32) - addKVSInt(vitem, kv, kConfigKeyInLength, mSetting.inLength) - addKVSInt(vitem, kv, kConfigKeyInQuantity, mSetting.inQuantity) - addKVSInt(vitem, kv, kConfigKeyInVariance, mSetting.inVariance) - addKVSInt(vitem, kv, kConfigKeyOutLength, mSetting.outLength) - addKVSInt(vitem, kv, kConfigKeyOutQuantity, mSetting.outQuantity) - addKVSInt(vitem, kv, kConfigKeyOutVariance, mSetting.outVariance) - - lst.push_back(vitem); - - return true; -} - -#undef addKVS -#undef addKVSUInt - -#define getKVSUInt(_kit, _key, _value) \ - else if (_kit->key == _key) {\ - std::istringstream is(_kit->value);\ - int tmp;\ - is >> tmp;\ - _value = (int8_t)tmp;\ - } - -bool p3I2pBob::loadList(std::list &load) -{ - RS_DBG4(""); - - for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { - RsConfigKeyValueSet *vitem = dynamic_cast(*it); - if(vitem != NULL) { - RS_STACK_MUTEX(mLock); - for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { - if (kit->key == kConfigKeyBOBEnable) - mSetting.enable = kit->value == "TRUE"; - else if (kit->key == kConfigKeyBOBKey) - mSetting.address.privateKey = kit->value; - else if (kit->key == kConfigKeyBOBAddr) - mSetting.address.base32 = kit->value; - getKVSUInt(kit, kConfigKeyInLength, mSetting.inLength) - getKVSUInt(kit, kConfigKeyInQuantity, mSetting.inQuantity) - getKVSUInt(kit, kConfigKeyInVariance, mSetting.inVariance) - getKVSUInt(kit, kConfigKeyOutLength, mSetting.outLength) - getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) - getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) - else - RS_DBG("unknown key: ", kit->key); - } - } - delete vitem; - } - - RS_STACK_MUTEX(mLock); - finalizeSettings_locked(); - mConfigLoaded = true; - - return true; -} - -#undef getKVSUInt - -void p3I2pBob::getBOBSettings(bobSettings *settings) -{ - if (settings == NULL) - return; - - RS_STACK_MUTEX(mLock); - *settings = mSetting; - -} - -void p3I2pBob::setBOBSettings(const bobSettings *settings) -{ - if (settings == NULL) - return; - - RS_STACK_MUTEX(mLock); - mSetting = *settings; - - IndicateConfigChanged(); - - // Note: - // We don't take care of updating a running BOB session here - // This can be done manually by stoping and restarting the session - - // Note2: - // In case there is no config yet to load - // finalize settings here instead - if (!mConfigLoaded) { - finalizeSettings_locked(); - mConfigLoaded = true; - } else { - updateSettings_locked(); - } -} - -void p3I2pBob::getStates(bobStates *bs) -{ - if (bs == NULL) - return; - - RS_STACK_MUTEX(mLock); - bs->cs = mState; - bs->ct = mTask; - bs->bs = mBOBState; - bs->tunnelName = mTunnelName; -} - -std::string p3I2pBob::executeCommand(const std::string &command) -{ - RS_DBG4("running: ", command); - - std::string copy = command; - copy.push_back('\n'); - - // send command - // there is only one thread that touches mSocket - no need for a lock - ::send(mSocket, copy.c_str(), copy.size(), 0); - - // receive answer (trailing new line is already removed!) - std::string ans = recv(); - - RS_DBG4("answer: ", ans); - - return ans; -} - -bool p3I2pBob::connectI2P() -{ - // there is only one thread that touches mSocket - no need for a lock - - if (mSocket != 0) { - RS_DBG("mSocket != 0"); - return false; - } - - // create socket - mSocket = unix_socket(PF_INET, SOCK_STREAM, 0); - if (mSocket < 0) - { - RS_DBG("Failed to open socket! Socket Error: ", socket_errorType(errno)); - return false; - } - - // connect - int err = unix_connect(mSocket, mI2PProxyAddr); - if (err != 0) { - RS_DBG("Failed to connect to BOB! Socket Error: ", socket_errorType(errno)); - return false; - } - - // receive hello msg - recv(); - - RS_DBG4("done"); - return true; -} - -bool p3I2pBob::disconnectI2P() -{ - // there is only one thread that touches mSocket - no need for a lock - - if (mSocket == 0) { - RS_DBG("mSocket == 0"); - return true; - } - - int err = unix_close(mSocket); - if (err != 0) { - RS_DBG("Failed to close socket! Socket Error: ", socket_errorType(errno)); - return false; - } - - RS_DBG4("done"); - mSocket = 0; - return true; -} - -std::string toString(const std::string &a, const int b) { - std::ostringstream oss; - oss << b; - return a + oss.str();; -} - -std::string toString(const std::string &a, const uint16_t b) { - return toString(a, (int)b); -} - -std::string toString(const std::string &a, const int8_t b) { - return toString(a, (int)b); -} - -void p3I2pBob::finalizeSettings_locked() -{ - RS_DBG4(""); - - sockaddr_storage_clear(mI2PProxyAddr); - // get i2p proxy addr - sockaddr_storage proxy; - mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); - - // overwrite port to bob port - sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); - sockaddr_storage_setport(mI2PProxyAddr, 2827); - - RS_DBG4("using ", mI2PProxyAddr); - RS_DBG4("using ", mSetting.address.base32); - - peerState ps; - mPeerMgr->getOwnNetStatus(ps); - - // setup commands - // new lines are appended later! - - // generate 8 characater long random suffix for name - constexpr size_t len = 8; - const std::string location = RsRandom::alphaNumeric(len); - RS_DBG4("using suffix ", location); - mTunnelName = "RetroShare-" + location; - - const std::string setnick = "setnick RetroShare-" + location; - const std::string getnick = "getnick RetroShare-" + location; - const std::string newkeys = "newkeys"; - const std::string getkeys = "getkeys"; - const std::string setkeys = "setkeys " + mSetting.address.privateKey; - const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); - const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); - const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); - const std::string outport = toString("outport ", sockaddr_storage_port(ps.localaddr)); - // length - const std::string inlength = toString("option inbound.length=", mSetting.inLength); - const std::string outlength = toString("option outbound.length=", mSetting.outLength); - // variance - const std::string invariance = toString("option inbound.lengthVariance=", mSetting.inVariance); - const std::string outvariance= toString("option outbound.lengthVariance=", mSetting.outVariance); - // quantity - const std::string inquantity = toString("option inbound.quantity=", mSetting.inQuantity); - const std::string outquantity= toString("option outbound.quantity=", mSetting.outQuantity); - const std::string quiet = "quiet true"; - const std::string start = "start"; - const std::string stop = "stop"; - const std::string clear = "clear"; - const std::string list = "list"; - const std::string quit = "quit"; - - // setup state machine - - // start chain - // -> A: server and client tunnel - mCommands[bsSetnickS] = {setnick, bsSetkeys}; - mCommands[bsSetkeys] = {setkeys, bsOuthost}; - mCommands[bsOuthost] = {outhost, bsOutport}; - mCommands[bsOutport] = {outport, bsInhost}; - // -> B: only client tunnel - mCommands[bsSetnickC] = {setnick, bsNewkeysC}; - mCommands[bsNewkeysC] = {newkeys, bsInhost}; - // -> both - mCommands[bsInhost] = {inhost, bsInport}; - mCommands[bsInport] = {inport, bsInlength}; - mCommands[bsInlength] = {inlength, bsOutlength}; - mCommands[bsOutlength] = {outlength, bsInvariance}; - mCommands[bsInvariance] = {invariance, bsOutvariance}; - mCommands[bsOutvariance]= {outvariance,bsInquantity}; - mCommands[bsInquantity] = {inquantity, bsOutquantity}; - mCommands[bsOutquantity]= {outquantity,bsQuiet}; - mCommands[bsQuiet] = {quiet, bsStart}; - mCommands[bsStart] = {start, bsQuit}; - mCommands[bsQuit] = {quit, bsCleared}; - - // stop chain - mCommands[bsGetnick] = {getnick, bsStop}; - mCommands[bsStop] = {stop, bsClear}; - mCommands[bsClear] = {clear, bsQuit}; - - // getkeys chain - mCommands[bsSetnickN] = {setnick, bsNewkeysN}; - mCommands[bsNewkeysN] = {newkeys, bsGetkeys}; - mCommands[bsGetkeys] = {getkeys, bsClear}; - - // list chain - mCommands[bsList] = {list, bsQuit}; -} - -void p3I2pBob::updateSettings_locked() -{ - RS_DBG4(""); - - sockaddr_storage proxy; - mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); - - peerState ps; - mPeerMgr->getOwnNetStatus(ps); - - const std::string setkeys = "setkeys " + mSetting.address.privateKey; - const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); - const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); - const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); - const std::string outport = toString("outport ", sockaddr_storage_port(ps.localaddr)); - - // length - const std::string inlength = toString("option inbound.length=", mSetting.inLength); - const std::string outlength = toString("option outbound.length=", mSetting.outLength); - // variance - const std::string invariance = toString("option inbound.lengthVariance=", mSetting.inVariance); - const std::string outvariance= toString("option outbound.lengthVariance=", mSetting.outVariance); - // quantity - const std::string inquantity = toString("option inbound.quantity=", mSetting.inQuantity); - const std::string outquantity= toString("option outbound.quantity=", mSetting.outQuantity); - - mCommands[bsSetkeys] = {setkeys, bsOuthost}; - mCommands[bsOuthost] = {outhost, bsOutport}; - mCommands[bsOutport] = {outport, bsInhost}; - mCommands[bsInhost] = {inhost, bsInport}; - mCommands[bsInport] = {inport, bsInlength}; - - mCommands[bsInlength] = {inlength, bsOutlength}; - mCommands[bsOutlength] = {outlength, bsInvariance}; - mCommands[bsInvariance] = {invariance, bsOutvariance}; - mCommands[bsOutvariance]= {outvariance,bsInquantity}; - mCommands[bsInquantity] = {inquantity, bsOutquantity}; - mCommands[bsOutquantity]= {outquantity,bsQuiet}; -} - -std::string p3I2pBob::recv() -{ - // BOB works line based - // -> \n indicates and of the line - - constexpr uint16_t bufferSize = 128; - char buffer[bufferSize]; - - std::string ans; - uint16_t retry = 10; - - do { - memset(buffer, 0, bufferSize); - - // peek at data - auto length = ::recv(mSocket, buffer, bufferSize, MSG_PEEK); - if (length <= 0) { - if (length < 0) { - // error - perror(__PRETTY_FUNCTION__); - } - retry--; - doSleep(sleepTimeRecv); - continue; - } - - // at least one byte was read - - // search for new line - auto bufferStr = std::string(buffer); - size_t pos = bufferStr.find('\n'); - - if (pos == std::string::npos) { - // no new line found -> more to read - - // sanity check - if (length != bufferSize) { - // expectation: a full buffer was peeked) - RS_DBG1("peeked less than bufferSize but also didn't found a new line character"); - } - // this should never happen - assert(length <= bufferSize); - } else { - // new line found -> end of message - - // calculate how much there is to read, read the \n, too! - length = pos + 1; - - // end loop - retry = 0; - } - - // now read for real - memset(buffer, 0, bufferSize); - length = ::recv(mSocket, buffer, length, 0); - bufferStr = std::string(buffer); - ans.append(bufferStr); - } while(retry > 0); - - return ans; -} diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.h b/libretroshare/src/services/autoproxy/p3i2pbob.h deleted file mode 100644 index 29bcbcb61..000000000 --- a/libretroshare/src/services/autoproxy/p3i2pbob.h +++ /dev/null @@ -1,256 +0,0 @@ -/******************************************************************************* - * libretroshare/src/services/autoproxy: p3i2pbob.h * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#ifndef P3I2PBOB_H -#define P3I2PBOB_H - -#include -#include -#include -#include "util/rstime.h" -#ifndef WINDOWS_SYS - #include -#endif - -#include "pqi/p3cfgmgr.h" -#include "services/autoproxy/rsautoproxymonitor.h" -#include "util/rsthreads.h" -#include "util/i2pcommon.h" - -/* - * This class implements I2P BOB (BASIC OPEN BRIDGE) communication to allow RS - * to automatically remote control I2P to setup the needed tunnel. - * BOB is a simple text-based interface: https://geti2p.net/en/docs/api/bob - * - * Note 1: - * One tunnel is enough even for hidden locations since it can be used - * bidirectional. (In contrast to what RS I2P users had to set up manually.) - * - * Note 2: - * BOB tunnels are no SOCKS tunnel. Therefore pqissli2pbob implements a simplified - * proxy specially for BOB tunnels. - * - * Note 3: - * BOB needs a unique name as an ID for each tunnel. - * We use 'RetroShare-' + 8 random base32 characters. - * - * Design: - * The service uses three state machines to manage its task: - * int stateMachineBOB(); - * mBOBState - * int stateMachineController(); - * mState - * mTask - * - * stateMachineBOB: - * This state machine manages the low level communication with BOB. It basically has a linked - * list (currently a implemented as a std::map) that contains a command and the next - * state. - * Each high level operation (start up / shut down / get keys) is represented by a - * chain of states. E.g. the chain to retrieve new keys: - * mCommands[bobState::setnickN] = {setnick, bobState::newkeysN}; - * mCommands[bobState::newkeysN] = {newkeys, bobState::getkeys}; - * mCommands[bobState::getkeys] = {getkeys, bobState::clear}; - * mCommands[bobState::clear] = {clear, bobState::quit}; - * mCommands[bobState::quit] = {quit, bobState::cleared}; - * - * stateMachineController: - * This state machine manages the high level tasks. - * It is controlled by mState and mTask. - * - * mTast: - * Tracks the high level operation (like start up). - * It will keep its value even when a task is done to track - * the requested BOB state. - * When other operations are performed like a conection check - * the last task gets backed up and is later restored again - * - * mState: - * This state lives only for one operation an manages the communication - * with the BOB instance. This is basically connecting, starting BOB - * protocol and disconnecting - * - * How a task looks like: - * 1) RS sets task using the ticket system - * 2) stateMachineController connects to BOBs control port, sets mBobState to a lists head - * 3) stateMachineBOB processes command chain - * 4) stateMachineBOB is done and sets mBobState to cleared signaling that the connection - * is cleared and can be closed - * 5) stateMachineController disconnects from BOBs control port and updates mState - */ - -/// -/// \brief The controllerState enum -/// States for the controller to keep track of what he is currently doing -enum controllerState { - csIdel, - csDoConnect, - csConnected, - csWaitForBob, - csDoDisconnect, - csDisconnected, - csError -}; - -/// -/// \brief The controllerTask enum -/// This state tracks the controllers tast (e.g. setup a BOB tunnel or shut down -/// an existing one). -enum controllerTask { - ctIdle, - ctRunSetUp, - ctRunShutDown, - ctRunGetKeys, - ctRunCheck -}; - -/// -/// \brief The bobState enum -/// One state for each message -/// -enum bobState { - bsCleared, - bsSetnickC, // chain head for only client tunnel - bsSetnickN, // chain head for getting new (server) keys - bsSetnickS, // chain head for client and server tunnel - bsGetnick, - bsNewkeysC, // part of chain for only client tunnel - bsNewkeysN, // part of chain for getting new (server) keys - bsGetkeys, - bsSetkeys, - bsInhost, - bsOuthost, - bsInport, - bsOutport, - bsInlength, - bsOutlength, - bsInvariance, - bsOutvariance, - bsInquantity, - bsOutquantity, - bsQuiet, - bsStart, - bsStop, - bsClear, - bsList, // chain head for 'list' command - bsQuit -}; - -/// -/// \brief The bobStateInfo struct -/// State machine with commands -/// \todo This could be replaced by a linked list instead of a map -struct bobStateInfo { - std::string command; - bobState nextState; -}; - -struct bobSettings : i2p::settings {}; - -/// -/// \brief The bobStates struct -/// This container struct is used to pass all states. -/// Additionally, the tunnel name is included to to show it in the GUI. -/// The advantage of a struct is that it can be forward declared. -struct bobStates { - bobState bs; - controllerState cs; - controllerTask ct; - - std::string tunnelName; -}; - -class p3PeerMgr; - -class p3I2pBob : public RsTickingThread, public p3Config, public autoProxyService -{ -public: - explicit p3I2pBob(p3PeerMgr *peerMgr); - - // autoProxyService interface -public: - bool isEnabled(); - bool initialSetup(std::string &addr, uint16_t &); - void processTaskAsync(taskTicket *ticket); - void processTaskSync(taskTicket *ticket); - - void threadTick() override; /// @see RsTickingThread - -private: - int stateMachineBOB(); - int stateMachineBOB_locked_failure(const std::string &answer, const bobStateInfo ¤tState); - - int stateMachineController(); - int stateMachineController_locked_idle(); - int stateMachineController_locked_connected(); - int stateMachineController_locked_disconnected(); - int stateMachineController_locked_error(); - - // p3Config interface -protected: - RsSerialiser *setupSerialiser(); - bool saveList(bool &cleanup, std::list &lst); - bool loadList(std::list &load); - -private: - // helpers - void getBOBSettings(bobSettings *settings); - void setBOBSettings(const bobSettings *settings); - void getStates(bobStates *bs); - - std::string executeCommand(const std::string &command); - bool connectI2P(); - bool disconnectI2P(); - - void finalizeSettings_locked(); - void updateSettings_locked(); - - std::string recv(); - - // states for state machines - controllerState mState; - controllerTask mTask; - // used to store old state when in error state - // mStateOld is also used as a flag when an error occured in BOB protocol - controllerState mStateOld; - // mTaskOld is used to keep the previous task (start up / shut down) when requesting keys or checking the connection - controllerTask mTaskOld; - bobSettings mSetting; - bobState mBOBState; - - // used variables - p3PeerMgr *mPeerMgr; - bool mConfigLoaded; - int mSocket; - rstime_t mLastProxyCheck; - sockaddr_storage mI2PProxyAddr; - std::map mCommands; - std::string mErrorMsg; - std::string mTunnelName; - - std::queue mPending; - taskTicket *mProcessing; - - // mutex - RsMutex mLock; -}; - -#endif // P3I2PBOB_H From 1fa16aa6eb93e8a254a7b1087ff35b19df1b5916 Mon Sep 17 00:00:00 2001 From: sehraf Date: Sun, 22 Nov 2020 11:52:48 +0100 Subject: [PATCH 09/20] SAM3: fix double free --- libretroshare/src/pqi/pqissli2psam3.cpp | 3 +- .../src/services/autoproxy/p3i2psam3.cpp | 62 +++++++++++++++---- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/libretroshare/src/pqi/pqissli2psam3.cpp b/libretroshare/src/pqi/pqissli2psam3.cpp index 41087a893..1e0f5d433 100644 --- a/libretroshare/src/pqi/pqissli2psam3.cpp +++ b/libretroshare/src/pqi/pqissli2psam3.cpp @@ -124,7 +124,8 @@ int pqissli2psam3::net_internal_close(int fd) // now to the actuall closing int ret = pqissl::net_internal_close(fd); - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::closeConnection, this, mConn), + + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::closeConnection, this, mConn); // finally cleanup mConn = 0; diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp index ed2b51ccd..0a0121df0 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.cpp +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -76,6 +76,13 @@ bool p3I2pSam3::initialSetup(std::string &addr, uint16_t &/*port*/) i2p::getKeyTypes(mSetting.address.publicKey, s, c); RS_INFO("received key", s, c); + // sanity check + auto pub = i2p::publicKeyFromPrivate(mSetting.address.privateKey); + if (pub != mSetting.address.publicKey) { + RS_WARN("public key does not match private key! fixing ..."); + mSetting.address.privateKey = pub; + } + IndicateConfigChanged(); } @@ -482,7 +489,7 @@ bool p3I2pSam3::startSession() stopSession(); } - auto session = new Sam3Session(); + auto session = (Sam3Session*)rs_malloc(sizeof (Sam3Session)); // add nick paramsStr.append("inbound.nickname=" + nick); // leading space is already there @@ -572,7 +579,7 @@ void p3I2pSam3::stopSession() RS_STACK_MUTEX(mLockSam3Access); sam3CloseSession(mSetting.session); - delete mSetting.session; + free(mSetting.session); mSetting.session = nullptr; mState = samStatus::samState::offline; @@ -687,46 +694,75 @@ void p3I2pSam3::establishConnection(taskTicket *ticket) void p3I2pSam3::closeConnection(taskTicket *ticket) { - Sam3Connection *con = static_cast(ticket->data); + Sam3Connection *conn = static_cast(ticket->data); if (mState == samStatus::samState::offline || !mSetting.session) { - // no session found, sam was likel stopped + // no session found, sam was likely stopped + RS_DBG2("no session found"); + + auto it = std::find(mInvalidConnections.begin(), mInvalidConnections.end(), conn); + if (it != mInvalidConnections.end()) { + // this is the expected case + mInvalidConnections.erase(it); + } else { + // this is unexpected but not a big deal, just warn + RS_WARN("cannot find connection in mInvalidConnections"); + + it = std::find(mValidConnections.begin(), mValidConnections.end(), conn); + if (it != mValidConnections.end()) { + mValidConnections.erase(it); + + // now it is getting even weirder, still not a big deal, just warn + RS_WARN("found connection in mValidConnections"); + } + } + + // when libsam3 has already handled closing of the connection - which should be the case here - the memory has been freed already (-> pointer is invalid) + conn = nullptr; } else { RS_STACK_MUTEX(mLock); bool callClose = true; // search in current connections - auto it = std::find(mValidConnections.begin(), mValidConnections.end(), con); + auto it = std::find(mValidConnections.begin(), mValidConnections.end(), conn); if (it != mValidConnections.end()) { + RS_DBG2("found valid connection"); mValidConnections.erase(it); } else { // search in old connections - it = std::find(mInvalidConnections.begin(), mInvalidConnections.end(), con); + it = std::find(mInvalidConnections.begin(), mInvalidConnections.end(), conn); if (it != mInvalidConnections.end()) { // old connection, just ignore. *should* be freed already + mInvalidConnections.erase(it); + + RS_DBG2("found old (invalid) connection"); + callClose = false; - con = nullptr; + conn = nullptr; } else { // weird RS_WARN("could'n find connection!"); // best thing we can do here callClose = false; - con = nullptr; + conn = nullptr; } } if (callClose) { + RS_DBG2("closing connection"); + RS_STACK_MUTEX(mLockSam3Access); - sam3CloseConnection(con); - con = nullptr; // freed by above call + sam3CloseConnection(conn); + conn = nullptr; // freed by above call } } - if (con) { - delete con; - con = nullptr; + if (conn) { + free(conn); + conn = nullptr; } + ticket->data = nullptr; rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); return; From 36e238951f3d3d1063364a8f4ad15f82cfe19de2 Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 26 Nov 2020 21:05:25 +0100 Subject: [PATCH 10/20] update p3i2psam --- .../src/services/autoproxy/p3i2psam3.cpp | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp index 0a0121df0..97e8b392c 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.cpp +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -74,19 +74,26 @@ bool p3I2pSam3::initialSetup(std::string &addr, uint16_t &/*port*/) } else { std::string s, c; i2p::getKeyTypes(mSetting.address.publicKey, s, c); - RS_INFO("received key", s, c); + RS_INFO("received key ", s, " ", c); + RS_INFO("public key: ", mSetting.address.publicKey); + RS_INFO("private key: ", mSetting.address.privateKey); + RS_INFO("address: ", i2p::keyToBase32Addr(mSetting.address.publicKey)); // sanity check auto pub = i2p::publicKeyFromPrivate(mSetting.address.privateKey); + RS_INFO("pub key derived: ", pub); + RS_INFO("address: ", i2p::keyToBase32Addr(pub)); if (pub != mSetting.address.publicKey) { RS_WARN("public key does not match private key! fixing ..."); - mSetting.address.privateKey = pub; + mSetting.address.publicKey = pub; } + mSetting.address.base32 = i2p::keyToBase32Addr(mSetting.address.publicKey); + IndicateConfigChanged(); } - addr = mSetting.address.base32 = i2p::keyToBase32Addr(mSetting.address.publicKey); + addr = mSetting.address.base32; return true; } @@ -527,9 +534,9 @@ bool p3I2pSam3::startSession() mSetting.address.base32 = i2p::keyToBase32Addr(session->pubkey); // do not overwrite the private key, if any!! - RS_DBG1("nick:", nick, "address:", mSetting.address.base32); - RS_DBG2(" myDestination.pub ", mSetting.address.publicKey); - RS_DBG2(" myDestination.priv", mSetting.address.privateKey); + RS_DBG1("nick: ", nick, " address: ", mSetting.address.base32); + RS_DBG2(" myDestination.pub ", mSetting.address.publicKey); + RS_DBG2(" myDestination.priv ", mSetting.address.privateKey); return true; } @@ -555,7 +562,6 @@ bool p3I2pSam3::startForwarding() RS_STACK_MUTEX(mLockSam3Access); int ret = sam3StreamForward(mSetting.session, sockaddr_storage_iptostring(ps.localaddr).c_str(), sockaddr_storage_port(ps.localaddr)); - if (ret < 0) { RS_DBG("forward failed, due to", mSetting.session->error); return false; @@ -639,8 +645,8 @@ void p3I2pSam3::lookupKey(taskTicket *ticket) int ret = sam3NameLookup(&ss, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, addr->base32.c_str()); if (ret < 0) { // get error - RS_DBG("key:", addr->base32); - RS_DBG("got error:", ss.error); + RS_DBG("key: ", addr->base32); + RS_DBG("got error: ", ss.error); rsAutoProxyMonitor::taskError(ticket); } else { addr->publicKey = ss.destkey; From 719adaae9bf7c6ce31aad74b059d1dc501f40565 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 27 Nov 2020 15:24:44 +0100 Subject: [PATCH 11/20] update i2psam3 --- libretroshare/src/services/autoproxy/p3i2psam3.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp index 97e8b392c..47123328a 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.cpp +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -64,12 +64,12 @@ bool p3I2pSam3::initialSetup(std::string &addr, uint16_t &/*port*/) RS_STACK_MUTEX(mLock); if (!mSetting.address.publicKey.empty() || !mSetting.address.privateKey.empty()) - RS_DBG("overwriting keys!"); + RS_WARN("overwriting keys!"); bool success = generateKey(mSetting.address.publicKey, mSetting.address.privateKey); if (!success) { - RS_DBG("failed to retrieve keys"); + RS_WARN("failed to retrieve keys"); return false; } else { std::string s, c; @@ -613,14 +613,14 @@ bool p3I2pSam3::generateKey(std::string &pub, std::string &priv) Sam3Session ss; if (0 > sam3GenerateKeys(&ss, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, Sam3SigType::EdDSA_SHA512_Ed25519)) { - RS_DBG("got error:", ss.error); + RS_DBG("got error: ", ss.error); return false; } pub = std::string(ss.pubkey); priv = std::string(ss.privkey); - RS_DBG2("publuc key / address", pub); - RS_DBG2("private key", priv); + RS_DBG2("publuc key / address ", pub); + RS_DBG2("private key ", priv); return true; } From 59fa96eb5ff6d225ee5548d2017a703f20c3794d Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 27 Nov 2020 15:44:49 +0100 Subject: [PATCH 12/20] remove own copy of libsam3 --- supportlibs/libsam3/Makefile | 43 - supportlibs/libsam3/README.md | 52 - supportlibs/libsam3/examples/libsam3 | 1 - supportlibs/libsam3/examples/sam3/README.md | 26 - supportlibs/libsam3/examples/sam3/dgramc.c | 116 -- supportlibs/libsam3/examples/sam3/dgrams.c | 113 -- .../libsam3/examples/sam3/namelookup.c | 43 - supportlibs/libsam3/examples/sam3/samtest.c | 51 - supportlibs/libsam3/examples/sam3/streamc.c | 87 - supportlibs/libsam3/examples/sam3/streamcs.c | 87 - supportlibs/libsam3/examples/sam3/streams.c | 72 - supportlibs/libsam3/examples/sam3/streamss.c | 72 - supportlibs/libsam3/examples/sam3a/test00.c | 178 -- supportlibs/libsam3/examples/sam3a/test_sc.c | 205 -- supportlibs/libsam3/examples/sam3a/test_ss.c | 236 --- supportlibs/libsam3/src/ext/tinytest.c | 466 ----- supportlibs/libsam3/src/ext/tinytest.h | 104 - supportlibs/libsam3/src/ext/tinytest_macros.h | 219 -- supportlibs/libsam3/src/libsam3/libsam3.c | 1345 ------------- supportlibs/libsam3/src/libsam3/libsam3.h | 314 --- supportlibs/libsam3/src/libsam3a/libsam3a.c | 1771 ----------------- supportlibs/libsam3/src/libsam3a/libsam3a.h | 372 ---- supportlibs/libsam3/test/libsam3/test_b32.c | 51 - supportlibs/libsam3/test/test.c | 12 - 24 files changed, 6036 deletions(-) delete mode 100644 supportlibs/libsam3/Makefile delete mode 100644 supportlibs/libsam3/README.md delete mode 120000 supportlibs/libsam3/examples/libsam3 delete mode 100644 supportlibs/libsam3/examples/sam3/README.md delete mode 100644 supportlibs/libsam3/examples/sam3/dgramc.c delete mode 100644 supportlibs/libsam3/examples/sam3/dgrams.c delete mode 100644 supportlibs/libsam3/examples/sam3/namelookup.c delete mode 100644 supportlibs/libsam3/examples/sam3/samtest.c delete mode 100644 supportlibs/libsam3/examples/sam3/streamc.c delete mode 100644 supportlibs/libsam3/examples/sam3/streamcs.c delete mode 100644 supportlibs/libsam3/examples/sam3/streams.c delete mode 100644 supportlibs/libsam3/examples/sam3/streamss.c delete mode 100644 supportlibs/libsam3/examples/sam3a/test00.c delete mode 100644 supportlibs/libsam3/examples/sam3a/test_sc.c delete mode 100644 supportlibs/libsam3/examples/sam3a/test_ss.c delete mode 100644 supportlibs/libsam3/src/ext/tinytest.c delete mode 100644 supportlibs/libsam3/src/ext/tinytest.h delete mode 100644 supportlibs/libsam3/src/ext/tinytest_macros.h delete mode 100644 supportlibs/libsam3/src/libsam3/libsam3.c delete mode 100644 supportlibs/libsam3/src/libsam3/libsam3.h delete mode 100644 supportlibs/libsam3/src/libsam3a/libsam3a.c delete mode 100644 supportlibs/libsam3/src/libsam3a/libsam3a.h delete mode 100644 supportlibs/libsam3/test/libsam3/test_b32.c delete mode 100644 supportlibs/libsam3/test/test.c diff --git a/supportlibs/libsam3/Makefile b/supportlibs/libsam3/Makefile deleted file mode 100644 index f8d92a090..000000000 --- a/supportlibs/libsam3/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -CFLAGS := -Wall -g -O2 -std=gnu99 - -SRCS := \ - src/libsam3/libsam3.c \ - src/libsam3a/libsam3a.c - -TESTS := \ - src/ext/tinytest.c \ - test/test.c \ - test/libsam3/test_b32.c - -LIB_OBJS := ${SRCS:.c=.o} -TEST_OBJS := ${TESTS:.c=.o} - -OBJS := ${LIB_OBJS} ${TEST_OBJS} - -LIB := libsam3.a - -all: build check - -check: libsam3-tests - ./libsam3-tests - -build: ${LIB} - -${LIB}: ${LIB_OBJS} - ${AR} -sr ${LIB} ${LIB_OBJS} - -libsam3-tests: ${TEST_OBJS} ${LIB} - ${CC} $^ -o $@ - -clean: - rm -f libsam3-tests ${LIB} ${OBJS} examples/sam3/samtest - -%.o: %.c Makefile - ${CC} ${CFLAGS} $(LDFLAGS) -c $< -o $@ - -fmt: - find . -name '*.c' -exec clang-format -i {} \; - find . -name '*.h' -exec clang-format -i {} \; - -info: - @echo $(AR) diff --git a/supportlibs/libsam3/README.md b/supportlibs/libsam3/README.md deleted file mode 100644 index cd6b291a5..000000000 --- a/supportlibs/libsam3/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# libsam3 - -[![Build Status](https://travis-ci.org/i2p/libsam3.svg?branch=master)](https://travis-ci.org/i2p/libsam3) - -A C library for the [SAM v3 API](https://geti2p.net/en/docs/api/samv3). - -## Development Status - -Maintained by idk, PRs are accepted on [I2P gitlab](https://i2pgit.org/i2p-hackers/libsam3)/[I2P gitlab](http://git.idk.i2p/i2p-hackers/libsam3), and on github at the official mirror repository: [i2p/libsam3](https://github.com/i2p/libsam3). - -## Usage - -Copy the two files from one of the following locations into your codebase: - -- `src/libsam3` - Synchronous implementation. -- `src/libsam3a` - Asynchronous implementation. - -See `examples/` for how to use various parts of the API. - -## Cross-Compiling for Windows from debian: - -Set your cross-compiler up: - -``` sh -export CC=x86_64-w64-mingw32-gcc -export CFLAGS='-Wall -O2 ' -export LDFLAGS='-lmingw32 -lws2_32 -lwsock32 -mwindows' -``` - -and run `make build`. Only libsam3 is available for Windows, libsam3a will be -made available at a later date. -` - -## Linker(Windows) - -When building for Windows remember to set the flags to link to the Winsock and Windows -libraries. - -`-lmingw32 -lws2_32 -lwsock32 -mwindows` - -This may apply when cross-compiling or compiling from Windows with mingw. - -## Cool Projects using libsam3 - -Are you using libsam3 to provide an a cool I2P based feature to your project? Let us know about it(and how -it uses libsam3) and we'll think about adding it here*! - - 1. [Retroshare](https://retroshare.cc) - -*Projects which are listed here must be actively maintained. Those which intentionally violate -the law or the rights of a person or persons directly won't be considered. Neither will obvious -trolling. The maintainer will make the final decision. diff --git a/supportlibs/libsam3/examples/libsam3 b/supportlibs/libsam3/examples/libsam3 deleted file mode 120000 index 136138896..000000000 --- a/supportlibs/libsam3/examples/libsam3 +++ /dev/null @@ -1 +0,0 @@ -../src/libsam3 \ No newline at end of file diff --git a/supportlibs/libsam3/examples/sam3/README.md b/supportlibs/libsam3/examples/sam3/README.md deleted file mode 100644 index 1228e4984..000000000 --- a/supportlibs/libsam3/examples/sam3/README.md +++ /dev/null @@ -1,26 +0,0 @@ -Examples -======== - -These examples show various ways of using libsam3 to enable i2p in your -application, and are also useful in other ways. If you implement an i2p -application library in another language, making variants basic tools wouldn't be -the worst way to make sure that it works. - -building --------- - -Once you have build the library in the root of this repository by running make -all, you can build all these examples at once by running - - make - -in this directory. I think it makes things easier to experiment with quickly. - -namelookup ----------- - -Namelookup uses the SAM API to find the base64 destination of an readable "jump" -or base32 i2p address. You can use it like this: - - ./lookup i2p-projekt.i2p - diff --git a/supportlibs/libsam3/examples/sam3/dgramc.c b/supportlibs/libsam3/examples/sam3/dgramc.c deleted file mode 100644 index 898a9aec1..000000000 --- a/supportlibs/libsam3/examples/sam3/dgramc.c +++ /dev/null @@ -1,116 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -// comment the following if you don't want to stress UDP with 'big' datagram -// seems that up to 32000 bytes can be used for localhost -// note that we need 516+6+? bytes for header; lets reserve 1024 bytes for it -#define BIG (32000 - 1024) - -#define KEYFILE "dgrams.key" - -int main(int argc, char *argv[]) { - Sam3Session ses; - char buf[1024]; - char destkey[517] = {0}; // 516 chars + \0 - int sz; - // - libsam3_debug = 1; - // - if (argc < 2) { - FILE *fl = fopen(KEYFILE, "rb"); - // - if (fl != NULL) { - if (fread(destkey, 516, 1, fl) == 1) { - fclose(fl); - goto ok; - } - fclose(fl); - } - printf("usage: dgramc PUBKEY\n"); - return 1; - } else { - if (strlen(argv[1]) != 516) { - fprintf(stderr, "FATAL: invalid key length!\n"); - return 1; - } - strcpy(destkey, argv[1]); - } - // -ok: - printf("creating session...\n"); - /* create TRANSIENT session with temporary disposible destination */ - if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, - SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_DGRAM, 4, - NULL) < 0) { - fprintf(stderr, "FATAL: can't create session\n"); - return 1; - } - /* send datagram */ - printf("sending test datagram...\n"); - if (sam3DatagramSend(&ses, destkey, "test", 4) < 0) { - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - /** receive reply */ - if ((sz = sam3DatagramReceive(&ses, buf, sizeof(buf) - 1)) < 0) { - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - /** null terminated string */ - buf[sz] = 0; - printf("received: [%s]\n", buf); - // -#ifdef BIG - { - char *big = calloc(BIG + 1024, sizeof(char)); - /** generate random string */ - sam3GenChannelName(big, BIG + 1023, BIG + 1023); - printf("sending BIG datagram...\n"); - if (sam3DatagramSend(&ses, destkey, big, BIG) < 0) { - free(big); - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - if ((sz = sam3DatagramReceive(&ses, big, BIG + 512)) < 0) { - free(big); - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - big[sz] = 0; - printf("received (%d): [%s]\n", sz, big); - free(big); - } -#endif - // - printf("sending quit datagram...\n"); - if (sam3DatagramSend(&ses, destkey, "quit", 4) < 0) { - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - if ((sz = sam3DatagramReceive(&ses, buf, sizeof(buf) - 1)) < 0) { - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - buf[sz] = 0; - printf("received: [%s]\n", buf); - // - sam3CloseSession(&ses); - return 0; -error: - sam3CloseSession(&ses); - return 1; -} diff --git a/supportlibs/libsam3/examples/sam3/dgrams.c b/supportlibs/libsam3/examples/sam3/dgrams.c deleted file mode 100644 index 1fe0fc0f7..000000000 --- a/supportlibs/libsam3/examples/sam3/dgrams.c +++ /dev/null @@ -1,113 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -#define KEYFILE "dgrams.key" - -int main(int argc, char *argv[]) { - Sam3Session ses; - char privkey[1024], pubkey[1024], buf[33 * 1024]; - - /** quit command */ - const char *quitstr = "quit"; - const size_t quitlen = strlen(quitstr); - - /** reply response */ - const char *replystr = "reply: "; - const size_t replylen = strlen(replystr); - - FILE *fl; - // - libsam3_debug = 1; - // - - /** generate new destination keypair */ - printf("generating keys...\n"); - if (sam3GenerateKeys(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, 4) < 0) { - fprintf(stderr, "FATAL: can't generate keys\n"); - return 1; - } - /** copy keypair into local buffer */ - strncpy(pubkey, ses.pubkey, sizeof(pubkey)); - strncpy(privkey, ses.privkey, sizeof(privkey)); - /** create sam session */ - printf("creating session...\n"); - if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, privkey, - SAM3_SESSION_DGRAM, 5, NULL) < 0) { - fprintf(stderr, "FATAL: can't create session\n"); - return 1; - } - /** make sure we have the right destination */ - // FIXME: probably not needed - if (strcmp(pubkey, ses.pubkey) != 0) { - fprintf(stderr, "FATAL: destination keys don't match\n"); - sam3CloseSession(&ses); - return 1; - } - /** print destination to stdout */ - printf("PUB KEY\n=======\n%s\n=======\n", ses.pubkey); - if ((fl = fopen(KEYFILE, "wb")) != NULL) { - /** write public key to keyfile */ - fwrite(pubkey, strlen(pubkey), 1, fl); - fclose(fl); - } - - /* now listen for UDP packets */ - printf("starting main loop...\n"); - for (;;) { - /** save replylen bytes for out reply at begining */ - char *datagramBuf = buf + replylen; - const size_t datagramMaxLen = sizeof(buf) - replylen; - int sz, isquit; - printf("waiting for datagram...\n"); - /** blocks until we get a UDP packet */ - if ((sz = sam3DatagramReceive(&ses, datagramBuf, datagramMaxLen) < 0)) { - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - /** ensure null terminated string */ - datagramBuf[sz] = 0; - /** print out datagram payload to user */ - printf("FROM\n====\n%s\n====\n", ses.destkey); - printf("SIZE=%d\n", sz); - printf("data: [%s]\n", datagramBuf); - /** check for "quit" */ - isquit = (sz == quitlen && memcmp(datagramBuf, quitstr, quitlen) == 0); - /** echo datagram back to sender with "reply: " at the beginning */ - memcpy(buf, replystr, replylen); - - if (sam3DatagramSend(&ses, ses.destkey, buf, sz + replylen) < 0) { - fprintf(stderr, "ERROR: %s\n", ses.error); - goto error; - } - /** if we got a quit command wait for 10 seconds and break out of the - * mainloop */ - if (isquit) { - printf("shutting down...\n"); - sleep(10); /* let dgram reach it's destination */ - break; - } - } - /** close session and delete keyfile */ - sam3CloseSession(&ses); - unlink(KEYFILE); - return 0; -error: - /** error case, close session, delete keyfile and return exit code 1 */ - sam3CloseSession(&ses); - unlink(KEYFILE); - return 1; -} diff --git a/supportlibs/libsam3/examples/sam3/namelookup.c b/supportlibs/libsam3/examples/sam3/namelookup.c deleted file mode 100644 index 772e4aceb..000000000 --- a/supportlibs/libsam3/examples/sam3/namelookup.c +++ /dev/null @@ -1,43 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -int main(int argc, char *argv[]) { - Sam3Session ses; - // - // - libsam3_debug = 1; - // - if (argc < 2) { - printf("usage: %s name [name...]\n", argv[0]); - return 1; - } - /** for each name in arguments ... */ - for (int n = 1; n < argc; ++n) { - if (!getenv("I2P_LOOKUP_QUIET")) { - fprintf(stdout, "%s ... ", argv[n]); - fflush(stdout); - } - /** do oneshot name lookup */ - if (sam3NameLookup(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, argv[n]) >= - 0) { - fprintf(stdout, "%s\n\n", ses.destkey); - } else { - fprintf(stdout, "FAILED [%s]\n", ses.error); - } - } - // - return 0; -} diff --git a/supportlibs/libsam3/examples/sam3/samtest.c b/supportlibs/libsam3/examples/sam3/samtest.c deleted file mode 100644 index b2d0b9983..000000000 --- a/supportlibs/libsam3/examples/sam3/samtest.c +++ /dev/null @@ -1,51 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -int main(int argc, char *argv[]) { - int fd; - SAMFieldList *rep = NULL; - const char *v; - // - libsam3_debug = 1; - // - // - if ((fd = sam3Handshake(NULL, 0, NULL)) < 0) - return 1; - // - if (sam3tcpPrintf(fd, "DEST GENERATE\n") < 0) - goto error; - rep = sam3ReadReply(fd); - // sam3DumpFieldList(rep); - if (!sam3IsGoodReply(rep, "DEST", "REPLY", "PUB", NULL)) - goto error; - if (!sam3IsGoodReply(rep, "DEST", "REPLY", "PRIV", NULL)) - goto error; - v = sam3FindField(rep, "PUB"); - printf("PUB KEY\n=======\n%s\n", v); - v = sam3FindField(rep, "PRIV"); - printf("PRIV KEY\n========\n%s\n", v); - sam3FreeFieldList(rep); - rep = NULL; - // - sam3FreeFieldList(rep); - sam3tcpDisconnect(fd); - return 0; -error: - sam3FreeFieldList(rep); - sam3tcpDisconnect(fd); - return 1; -} diff --git a/supportlibs/libsam3/examples/sam3/streamc.c b/supportlibs/libsam3/examples/sam3/streamc.c deleted file mode 100644 index 672831c3a..000000000 --- a/supportlibs/libsam3/examples/sam3/streamc.c +++ /dev/null @@ -1,87 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -#define KEYFILE "streams.key" - -int main(int argc, char *argv[]) { - Sam3Session ses; - Sam3Connection *conn; - char cmd[1024], destkey[617]; // 616 chars + \0 - // - libsam3_debug = 1; - // - memset(destkey, 0, sizeof(destkey)); - // - if (argc < 2) { - FILE *fl = fopen(KEYFILE, "rb"); - // - if (fl != NULL) { - if (fread(destkey, 616, 1, fl) == 1) { - fclose(fl); - goto ok; - } - fclose(fl); - } - printf("usage: streamc PUBKEY\n"); - return 1; - } else { - if (!sam3CheckValidKeyLength(argv[1])) { - fprintf(stderr, "FATAL: invalid key length! %s %lu\n", argv[1], - strlen(argv[1])); - return 1; - } - strcpy(destkey, argv[1]); - } - // -ok: - printf("creating session...\n"); - // create TRANSIENT session - if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, - SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, 4, - NULL) < 0) { - fprintf(stderr, "FATAL: can't create session\n"); - return 1; - } - // - printf("connecting...\n"); - if ((conn = sam3StreamConnect(&ses, destkey)) == NULL) { - fprintf(stderr, "FATAL: can't connect: %s\n", ses.error); - sam3CloseSession(&ses); - return 1; - } - // - // now waiting for incoming connection - printf("sending test command...\n"); - if (sam3tcpPrintf(conn->fd, "test\n") < 0) - goto error; - if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) - goto error; - printf("echo: %s\n", cmd); - // - printf("sending quit command...\n"); - if (sam3tcpPrintf(conn->fd, "quit\n") < 0) - goto error; - // - sam3CloseConnection(conn); - sam3CloseSession(&ses); - return 0; -error: - fprintf(stderr, "FATAL: some error occured!\n"); - sam3CloseConnection(conn); - sam3CloseSession(&ses); - return 1; -} diff --git a/supportlibs/libsam3/examples/sam3/streamcs.c b/supportlibs/libsam3/examples/sam3/streamcs.c deleted file mode 100644 index 25bd22072..000000000 --- a/supportlibs/libsam3/examples/sam3/streamcs.c +++ /dev/null @@ -1,87 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -#define KEYFILE "streams.key" - -int main(int argc, char *argv[]) { - Sam3Session ses; - Sam3Connection *conn; - char cmd[1024], destkey[617]; // 616 chars + \0 - // - libsam3_debug = 1; - // - memset(destkey, 0, sizeof(destkey)); - // - if (argc < 2) { - FILE *fl = fopen(KEYFILE, "rb"); - // - if (fl != NULL) { - if (fread(destkey, 616, 1, fl) == 1) { - fclose(fl); - goto ok; - } - fclose(fl); - } - printf("usage: streamc PUBKEY\n"); - return 1; - } else { - if (!sam3CheckValidKeyLength(argv[1])) { - fprintf(stderr, "FATAL: invalid key length! %s %lu\n", argv[1], - strlen(argv[1])); - return 1; - } - strcpy(destkey, argv[1]); - } - // -ok: - printf("creating session...\n"); - // create TRANSIENT session - if (sam3CreateSilentSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, - SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, - 4, NULL) < 0) { - fprintf(stderr, "FATAL: can't create session\n"); - return 1; - } - // - printf("connecting...\n"); - if ((conn = sam3StreamConnect(&ses, destkey)) == NULL) { - fprintf(stderr, "FATAL: can't connect: %s\n", ses.error); - sam3CloseSession(&ses); - return 1; - } - // - // now waiting for incoming connection - printf("sending test command...\n"); - if (sam3tcpPrintf(conn->fd, "test\n") < 0) - goto error; - if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) - goto error; - printf("echo: %s\n", cmd); - // - printf("sending quit command...\n"); - if (sam3tcpPrintf(conn->fd, "quit\n") < 0) - goto error; - // - sam3CloseConnection(conn); - sam3CloseSession(&ses); - return 0; -error: - fprintf(stderr, "FATAL: some error occured!\n"); - sam3CloseConnection(conn); - sam3CloseSession(&ses); - return 1; -} diff --git a/supportlibs/libsam3/examples/sam3/streams.c b/supportlibs/libsam3/examples/sam3/streams.c deleted file mode 100644 index 2fa000c1c..000000000 --- a/supportlibs/libsam3/examples/sam3/streams.c +++ /dev/null @@ -1,72 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -#define KEYFILE "streams.key" - -int main(int argc, char *argv[]) { - Sam3Session ses; - Sam3Connection *conn; - FILE *fl; - // - libsam3_debug = 1; - // - printf("creating session...\n"); - // create TRANSIENT session - if (sam3CreateSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, - SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, 4, - NULL) < 0) { - fprintf(stderr, "FATAL: can't create session\n"); - return 1; - } - // - printf("PUB KEY\n=======\n%s\n=======\n", ses.pubkey); - if ((fl = fopen(KEYFILE, "wb")) != NULL) { - fwrite(ses.pubkey, strlen(ses.pubkey), 1, fl); - fclose(fl); - } - // - printf("starting stream acceptor...\n"); - if ((conn = sam3StreamAccept(&ses)) == NULL) { - fprintf(stderr, "FATAL: can't accept: %s\n", ses.error); - sam3CloseSession(&ses); - return 1; - } - printf("FROM\n====\n%s\n====\n", conn->destkey); - // - printf("starting main loop...\n"); - for (;;) { - char cmd[256]; - // - if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) - goto error; - printf("cmd: [%s]\n", cmd); - if (strcmp(cmd, "quit") == 0) - break; - // echo command - if (sam3tcpPrintf(conn->fd, "re: %s\n", cmd) < 0) - goto error; - } - // - sam3CloseSession(&ses); - unlink(KEYFILE); - return 0; -error: - fprintf(stderr, "FATAL: some error occured!\n"); - sam3CloseSession(&ses); - unlink(KEYFILE); - return 1; -} diff --git a/supportlibs/libsam3/examples/sam3/streamss.c b/supportlibs/libsam3/examples/sam3/streamss.c deleted file mode 100644 index ffbc974da..000000000 --- a/supportlibs/libsam3/examples/sam3/streamss.c +++ /dev/null @@ -1,72 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include "../libsam3/libsam3.h" - -#define KEYFILE "streams.key" - -int main(int argc, char *argv[]) { - Sam3Session ses; - Sam3Connection *conn; - FILE *fl; - // - libsam3_debug = 1; - // - printf("creating session...\n"); - // create TRANSIENT session - if (sam3CreateSilentSession(&ses, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, - SAM3_DESTINATION_TRANSIENT, SAM3_SESSION_STREAM, - 4, NULL) < 0) { - fprintf(stderr, "FATAL: can't create session\n"); - return 1; - } - // - printf("PUB KEY\n=======\n%s\n=======\n", ses.pubkey); - if ((fl = fopen(KEYFILE, "wb")) != NULL) { - fwrite(ses.pubkey, strlen(ses.pubkey), 1, fl); - fclose(fl); - } - // - printf("starting stream acceptor...\n"); - if ((conn = sam3StreamAccept(&ses)) == NULL) { - fprintf(stderr, "FATAL: can't accept: %s\n", ses.error); - sam3CloseSession(&ses); - return 1; - } - printf("FROM\n====\n%s\n====\n", conn->destkey); - // - printf("starting main loop...\n"); - for (;;) { - char cmd[256]; - // - if (sam3tcpReceiveStr(conn->fd, cmd, sizeof(cmd)) < 0) - goto error; - printf("cmd: [%s]\n", cmd); - if (strcmp(cmd, "quit") == 0) - break; - // echo command - if (sam3tcpPrintf(conn->fd, "re: %s\n", cmd) < 0) - goto error; - } - // - sam3CloseSession(&ses); - unlink(KEYFILE); - return 0; -error: - fprintf(stderr, "FATAL: some error occured!\n"); - sam3CloseSession(&ses); - unlink(KEYFILE); - return 1; -} diff --git a/supportlibs/libsam3/examples/sam3a/test00.c b/supportlibs/libsam3/examples/sam3a/test00.c deleted file mode 100644 index 5596053e9..000000000 --- a/supportlibs/libsam3/examples/sam3a/test00.c +++ /dev/null @@ -1,178 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../libsam3a/libsam3a.h" - -//////////////////////////////////////////////////////////////////////////////// -static void scbErrorClose(Sam3ASession *ses) { - fprintf(stderr, - "\n===============================\nSESION_ERROR: " - "[%s]\n===============================\n", - ses->error); - sam3aCloseSession(ses); // it's safe here -} - -static void scbNRCreated(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nNAME RESOLVED: [%s]\n", - ses->params); - fprintf(stderr, "PUB: %s\n===============================\n", ses->destkey); - sam3aCloseSession(ses); // it's safe here -} - -static const Sam3ASessionCallbacks scbNR = { - .cbError = scbErrorClose, - .cbCreated = scbNRCreated, - .cbDisconnected = NULL, - .cbDatagramRead = NULL, - .cbDestroy = NULL, -}; - -//////////////////////////////////////////////////////////////////////////////// -static void scbKGCreated(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nKEYS GENERATED\n"); - fprintf(stderr, "\rPRIV: %s\n", ses->privkey); - fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); - sam3aCloseSession(ses); // it's safe here -} - -static const Sam3ASessionCallbacks scbKG = { - .cbError = scbErrorClose, - .cbCreated = scbKGCreated, - .cbDisconnected = NULL, - .cbDatagramRead = NULL, - .cbDestroy = NULL, -}; - -//////////////////////////////////////////////////////////////////////////////// -static void scbError(Sam3ASession *ses) { - fprintf(stderr, - "\n===============================\nSESION_ERROR: " - "[%s]\n===============================\n", - ses->error); -} - -static void scbCreated(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nSESION_CREATED\n"); - fprintf(stderr, "\rPRIV: %s\n", ses->privkey); - fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); - sam3aCancelSession(ses); // it's safe here -} - -static void scbDisconnected(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nSESION_DISCONNECTED\n====" - "===========================\n"); -} - -static void scbDGramRead(Sam3ASession *ses, const void *buf, int bufsize) { - fprintf(stderr, "\n===============================\nSESION_DATAGRAM_READ\n===" - "============================\n"); -} - -static void scbDestroy(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nSESION_DESTROYED\n=======" - "========================\n"); -} - -/** callbacks for our SAM session */ -static const Sam3ASessionCallbacks scb = { - .cbError = scbError, - .cbCreated = scbCreated, - .cbDisconnected = scbDisconnected, - .cbDatagramRead = scbDGramRead, - .cbDestroy = scbDestroy, -}; - -//////////////////////////////////////////////////////////////////////////////// -#define HOST SAM3A_HOST_DEFAULT -//#define HOST "google.com" - -int main(int argc, char *argv[]) { - Sam3ASession ses, snr, skg; - // - // libsam3a_debug = 1; - // - if (sam3aCreateSession(&ses, &scb, HOST, SAM3A_PORT_DEFAULT, - SAM3A_DESTINATION_TRANSIENT, - SAM3A_SESSION_STREAM) < 0) { - fprintf(stderr, "FATAL: can't create main session!\n"); - return 1; - } - // generate keys - if (sam3aGenerateKeys(&skg, &scbKG, HOST, SAM3A_PORT_DEFAULT) < 0) { - sam3aCloseSession(&ses); - fprintf(stderr, "FATAL: can't create keygen session!\n"); - return 1; - } - // do a name lookup for zzz.i2p - if (sam3aNameLookup(&snr, &scbNR, HOST, SAM3A_PORT_DEFAULT, "zzz.i2p") < 0) { - sam3aCloseSession(&skg); - sam3aCloseSession(&ses); - fprintf(stderr, "FATAL: can't create name resolving session!\n"); - return 1; - } - // while we have sessions ... - while (sam3aIsActiveSession(&ses) || sam3aIsActiveSession(&snr) || - sam3aIsActiveSession(&skg)) { - fd_set rds, wrs; - int res, maxfd = 0; - struct timeval to; - // set up file descriptors for select() - FD_ZERO(&rds); - FD_ZERO(&wrs); - // obtain the maximum fd for select() - if (sam3aIsActiveSession(&ses) && - (maxfd = sam3aAddSessionToFDS(&ses, -1, &rds, &wrs)) < 0) - break; - if (sam3aIsActiveSession(&snr) && - (maxfd = sam3aAddSessionToFDS(&snr, -1, &rds, &wrs)) < 0) - break; - if (sam3aIsActiveSession(&skg) && - (maxfd = sam3aAddSessionToFDS(&skg, -1, &rds, &wrs)) < 0) - break; - // set timeout to 1 second - sam3ams2timeval(&to, 1000); - // call select() - res = select(maxfd + 1, &rds, &wrs, NULL, &to); - if (res < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, "FATAL: select() error!\n"); - break; - } - if (res == 0) { - // idle, no activity - fprintf(stdout, "."); - fflush(stdout); - } else { - // we have activity, process io - if (sam3aIsActiveSession(&ses)) - sam3aProcessSessionIO(&ses, &rds, &wrs); - if (sam3aIsActiveSession(&snr)) - sam3aProcessSessionIO(&snr, &rds, &wrs); - if (sam3aIsActiveSession(&skg)) - sam3aProcessSessionIO(&skg, &rds, &wrs); - } - } - // close seessions - sam3aCloseSession(&ses); - sam3aCloseSession(&skg); - sam3aCloseSession(&snr); - // exit - return 0; -} diff --git a/supportlibs/libsam3/examples/sam3a/test_sc.c b/supportlibs/libsam3/examples/sam3a/test_sc.c deleted file mode 100644 index 8feeb5d52..000000000 --- a/supportlibs/libsam3/examples/sam3a/test_sc.c +++ /dev/null @@ -1,205 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../libsam3a/libsam3a.h" - -//////////////////////////////////////////////////////////////////////////////// -#define KEYFILE "streams.key" - -//////////////////////////////////////////////////////////////////////////////// -static void ccbError(Sam3AConnection *ct) { - fprintf(stderr, - "\n===============================\nCONNECTION_ERROR: " - "[%s]\n===============================\n", - ct->error); -} - -static void ccbDisconnected(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_" - "DISCONNECTED\n===============================\n"); -} - -static void ccbConnected(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_CONNECTED\n===" - "============================\n"); - // sam3aCancelConnection(ct); // cbSent() will not be called -} - -static void ccbAccepted(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_ACCEPTED\n====" - "===========================\n"); -} - -static void ccbSent(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_WANTBYTES\n===" - "============================\n"); - // sam3aCancelConnection(ct); - // sam3aCancelSession(ct->ses); // hehe - fprintf(stderr, "(%p)\n", ct->udata); - // - switch ((intptr_t)ct->udata) { - case 0: - if (sam3aSend(ct, "test\n", -1) < 0) { - fprintf(stderr, "SEND ERROR!\n"); - sam3aCancelSession(ct->ses); // hehe - } - break; - case 1: - if (sam3aSend(ct, "quit\n", -1) < 0) { - fprintf(stderr, "SEND ERROR!\n"); - sam3aCancelSession(ct->ses); // hehe - } - break; - default: - return; - } - ct->udata = (void *)(((intptr_t)ct->udata) + 1); -} - -static void ccbRead(Sam3AConnection *ct, const void *buf, int bufsize) { - fprintf(stderr, - "\n===============================\nCONNECTION_GOTBYTES " - "(%d)\n===============================\n", - bufsize); -} - -static void ccbDestroy(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_DESTROY\n=====" - "==========================\n"); -} - -static const Sam3AConnectionCallbacks ccb = { - .cbError = ccbError, - .cbDisconnected = ccbDisconnected, - .cbConnected = ccbConnected, - .cbAccepted = ccbAccepted, - .cbSent = ccbSent, - .cbRead = ccbRead, - .cbDestroy = ccbDestroy, -}; - -//////////////////////////////////////////////////////////////////////////////// -static void scbError(Sam3ASession *ses) { - fprintf(stderr, - "\n===============================\nSESION_ERROR: " - "[%s]\n===============================\n", - ses->error); -} - -static void scbCreated(Sam3ASession *ses) { - char destkey[517]; - FILE *fl; - // - fprintf(stderr, "\n===============================\nSESION_CREATED\n"); - fprintf(stderr, "\rPRIV: %s\n", ses->privkey); - fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); - // - fl = fopen(KEYFILE, "rb"); - // - if (fl == NULL) { - fprintf(stderr, "ERROR: NO KEY FILE!\n"); - sam3aCancelSession(ses); - return; - } - if (fread(destkey, 516, 1, fl) != 1) { - fprintf(stderr, "ERROR: INVALID KEY FILE!\n"); - fclose(fl); - sam3aCancelSession(ses); - return; - } - fclose(fl); - destkey[516] = 0; - if (sam3aStreamConnect(ses, &ccb, destkey) == NULL) { - fprintf(stderr, "ERROR: CAN'T CREATE CONNECTION!\n"); - sam3aCancelSession(ses); - return; - } - fprintf(stderr, "GOON: creating connection...\n"); -} - -static void scbDisconnected(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nSESION_DISCONNECTED\n====" - "===========================\n"); -} - -static void scbDGramRead(Sam3ASession *ses, const void *buf, int bufsize) { - fprintf(stderr, "\n===============================\nSESION_DATAGRAM_READ\n===" - "============================\n"); -} - -static void scbDestroy(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nSESION_DESTROYED\n=======" - "========================\n"); -} - -static const Sam3ASessionCallbacks scb = { - .cbError = scbError, - .cbCreated = scbCreated, - .cbDisconnected = scbDisconnected, - .cbDatagramRead = scbDGramRead, - .cbDestroy = scbDestroy, -}; - -//////////////////////////////////////////////////////////////////////////////// -#define HOST SAM3A_HOST_DEFAULT -//#define HOST "google.com" - -int main(int argc, char *argv[]) { - Sam3ASession ses; - // - libsam3a_debug = 0; - // - if (sam3aCreateSession(&ses, &scb, HOST, SAM3A_PORT_DEFAULT, - SAM3A_DESTINATION_TRANSIENT, - SAM3A_SESSION_STREAM) < 0) { - fprintf(stderr, "FATAL: can't create main session!\n"); - return 1; - } - // - while (sam3aIsActiveSession(&ses)) { - fd_set rds, wrs; - int res, maxfd = 0; - struct timeval to; - // - FD_ZERO(&rds); - FD_ZERO(&wrs); - if (sam3aIsActiveSession(&ses) && - (maxfd = sam3aAddSessionToFDS(&ses, -1, &rds, &wrs)) < 0) - break; - sam3ams2timeval(&to, 1000); - res = select(maxfd + 1, &rds, &wrs, NULL, &to); - if (res < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, "FATAL: select() error!\n"); - break; - } - if (res == 0) { - fprintf(stdout, "."); - fflush(stdout); - } else { - if (sam3aIsActiveSession(&ses)) - sam3aProcessSessionIO(&ses, &rds, &wrs); - } - } - // - sam3aCloseSession(&ses); - // - return 0; -} diff --git a/supportlibs/libsam3/examples/sam3a/test_ss.c b/supportlibs/libsam3/examples/sam3a/test_ss.c deleted file mode 100644 index c2ff3399c..000000000 --- a/supportlibs/libsam3/examples/sam3a/test_ss.c +++ /dev/null @@ -1,236 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../libsam3a/libsam3a.h" - -//////////////////////////////////////////////////////////////////////////////// -#define KEYFILE "streams.key" - -//////////////////////////////////////////////////////////////////////////////// -typedef struct { - char *str; - int strsize; - int strused; - int doQuit; -} ConnData; - -static void cdAppendChar(ConnData *d, char ch) { - if (d->strused + 1 >= d->strsize) { - // fuck errors - d->strsize = d->strused + 1024; - d->str = realloc(d->str, d->strsize + 1); - } - d->str[d->strused++] = ch; - d->str[d->strused] = 0; -} - -//////////////////////////////////////////////////////////////////////////////// -static void ccbError(Sam3AConnection *ct) { - fprintf(stderr, - "\n===============================\nCONNECTION_ERROR: " - "[%s]\n===============================\n", - ct->error); -} - -static void ccbDisconnected(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_" - "DISCONNECTED\n===============================\n"); -} - -static void ccbConnected(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_CONNECTED\n===" - "============================\n"); - // sam3aCancelConnection(ct); // cbSent() will not be called -} - -static void ccbAccepted(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_ACCEPTED\n====" - "===========================\n"); - fprintf(stderr, "FROM: %s\n===============================\n", ct->destkey); -} - -static void ccbSent(Sam3AConnection *ct) { - ConnData *d = (ConnData *)ct->udata; - // - fprintf(stderr, "\n===============================\nCONNECTION_WANTBYTES\n===" - "============================\n"); - if (d->doQuit) { - sam3aCancelSession(ct->ses); // hehe - } -} - -static void ccbRead(Sam3AConnection *ct, const void *buf, int bufsize) { - const char *b = (const char *)buf; - ConnData *d = (ConnData *)ct->udata; - // - fprintf(stderr, - "\n===============================\nCONNECTION_GOTBYTES " - "(%d)\n===============================\n", - bufsize); - while (bufsize > 0) { - cdAppendChar(ct->udata, *b); - if (*b == '\n') { - fprintf(stderr, "cmd: %s", d->str); - if (strcasecmp(d->str, "quit\n") == 0) - d->doQuit = 1; - if (sam3aSend(ct, d->str, -1) < 0) { - // sam3aCancelConnection(ct); // hehe - sam3aCancelSession(ct->ses); // hehe - return; - } - d->str[0] = 0; - d->strused = 0; - } - ++b; - --bufsize; - } -} - -static void ccbDestroy(Sam3AConnection *ct) { - fprintf(stderr, "\n===============================\nCONNECTION_DESTROY\n=====" - "==========================\n"); - if (ct->udata != NULL) { - ConnData *d = (ConnData *)ct->udata; - // - if (d->str != NULL) - free(d->str); - free(d); - } -} - -static const Sam3AConnectionCallbacks ccb = { - .cbError = ccbError, - .cbDisconnected = ccbDisconnected, - .cbConnected = ccbConnected, - .cbAccepted = ccbAccepted, - .cbSent = ccbSent, - .cbRead = ccbRead, - .cbDestroy = ccbDestroy, -}; - -//////////////////////////////////////////////////////////////////////////////// -static void scbError(Sam3ASession *ses) { - fprintf(stderr, - "\n===============================\nSESION_ERROR: " - "[%s]\n===============================\n", - ses->error); -} - -static void scbCreated(Sam3ASession *ses) { - FILE *fl; - Sam3AConnection *conn; - // - fprintf(stderr, "\n===============================\nSESION_CREATED\n"); - fprintf(stderr, "\rPRIV: %s\n", ses->privkey); - fprintf(stderr, "\nPUB: %s\n===============================\n", ses->pubkey); - // - fl = fopen(KEYFILE, "wb"); - // - if (fl == NULL) { - fprintf(stderr, "ERROR: CAN'T CREATE KEY FILE!\n"); - sam3aCancelSession(ses); - return; - } - if (fwrite(ses->pubkey, 516, 1, fl) != 1) { - fprintf(stderr, "ERROR: CAN'T WRITE KEY FILE!\n"); - fclose(fl); - sam3aCancelSession(ses); - return; - } - fclose(fl); - if ((conn = sam3aStreamAccept(ses, &ccb)) == NULL) { - fprintf(stderr, "ERROR: CAN'T CREATE CONNECTION!\n"); - sam3aCancelSession(ses); - return; - } - // - conn->udata = calloc(1, sizeof(ConnData)); - fprintf(stderr, "GOON: accepting connection...\n"); -} - -static void scbDisconnected(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nSESION_DISCONNECTED\n====" - "===========================\n"); -} - -static void scbDGramRead(Sam3ASession *ses, const void *buf, int bufsize) { - fprintf(stderr, "\n===============================\nSESION_DATAGRAM_READ\n===" - "============================\n"); -} - -static void scbDestroy(Sam3ASession *ses) { - fprintf(stderr, "\n===============================\nSESION_DESTROYED\n=======" - "========================\n"); -} - -static const Sam3ASessionCallbacks scb = { - .cbError = scbError, - .cbCreated = scbCreated, - .cbDisconnected = scbDisconnected, - .cbDatagramRead = scbDGramRead, - .cbDestroy = scbDestroy, -}; - -//////////////////////////////////////////////////////////////////////////////// -#define HOST SAM3A_HOST_DEFAULT -//#define HOST "google.com" - -int main(int argc, char *argv[]) { - Sam3ASession ses; - // - libsam3a_debug = 0; - // - if (sam3aCreateSession(&ses, &scb, HOST, SAM3A_PORT_DEFAULT, - SAM3A_DESTINATION_TRANSIENT, - SAM3A_SESSION_STREAM) < 0) { - fprintf(stderr, "FATAL: can't create main session!\n"); - return 1; - } - // - while (sam3aIsActiveSession(&ses)) { - fd_set rds, wrs; - int res, maxfd = 0; - struct timeval to; - // - FD_ZERO(&rds); - FD_ZERO(&wrs); - if (sam3aIsActiveSession(&ses) && - (maxfd = sam3aAddSessionToFDS(&ses, -1, &rds, &wrs)) < 0) - break; - sam3ams2timeval(&to, 1000); - res = select(maxfd + 1, &rds, &wrs, NULL, &to); - if (res < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, "FATAL: select() error!\n"); - break; - } - if (res == 0) { - fprintf(stdout, "."); - fflush(stdout); - } else { - if (sam3aIsActiveSession(&ses)) - sam3aProcessSessionIO(&ses, &rds, &wrs); - } - } - // - sam3aCloseSession(&ses); - // - return 0; -} diff --git a/supportlibs/libsam3/src/ext/tinytest.c b/supportlibs/libsam3/src/ext/tinytest.c deleted file mode 100644 index 252f03051..000000000 --- a/supportlibs/libsam3/src/ext/tinytest.c +++ /dev/null @@ -1,466 +0,0 @@ -/* tinytest.c -- Copyright 2009-2012 Nick Mathewson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifdef TINYTEST_LOCAL -#include "tinytest_local.h" -#endif - -#include -#include -#include -#include - -#ifndef NO_FORKING - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) -#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ - __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) -/* Workaround for a stupid bug in OSX 10.6 */ -#define FORK_BREAKS_GCOV -#include -#endif -#endif - -#endif /* !NO_FORKING */ - -#ifndef __GNUC__ -#define __attribute__(x) -#endif - -#include "tinytest.h" -#include "tinytest_macros.h" - -#define LONGEST_TEST_NAME 16384 - -static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ -static int n_ok = 0; /**< Number of tests that have passed */ -static int n_bad = 0; /**< Number of tests that have failed. */ -static int n_skipped = 0; /**< Number of tests that have been skipped. */ - -static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ -static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ -static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ -const char *verbosity_flag = ""; - -const struct testlist_alias_t *cfg_aliases = NULL; - -enum outcome { SKIP = 2, OK = 1, FAIL = 0 }; -static enum outcome cur_test_outcome = FAIL; -const char *cur_test_prefix = NULL; /**< prefix of the current test group */ -/** Name of the current test, if we haven't logged is yet. Used for --quiet */ -const char *cur_test_name = NULL; - -#ifdef _WIN32 -/* Copy of argv[0] for win32. */ -static char commandname[MAX_PATH + 1]; -#endif - -static void usage(struct testgroup_t *groups, int list_groups) - __attribute__((noreturn)); -static int process_test_option(struct testgroup_t *groups, const char *test); - -static enum outcome testcase_run_bare_(const struct testcase_t *testcase) { - void *env = NULL; - enum outcome outcome; - if (testcase->setup) { - env = testcase->setup->setup_fn(testcase); - if (!env) - return FAIL; - else if (env == (void *)TT_SKIP) - return SKIP; - } - - cur_test_outcome = OK; - testcase->fn(env); - outcome = cur_test_outcome; - - if (testcase->setup) { - if (testcase->setup->cleanup_fn(testcase, env) == 0) - outcome = FAIL; - } - - return outcome; -} - -#define MAGIC_EXITCODE 42 - -#ifndef NO_FORKING - -static enum outcome testcase_run_forked_(const struct testgroup_t *group, - const struct testcase_t *testcase) { -#ifdef _WIN32 - /* Fork? On Win32? How primitive! We'll do what the smart kids do: - we'll invoke our own exe (whose name we recall from the command - line) with a command line that tells it to run just the test we - want, and this time without forking. - - (No, threads aren't an option. The whole point of forking is to - share no state between tests.) - */ - int ok; - char buffer[LONGEST_TEST_NAME + 256]; - STARTUPINFOA si; - PROCESS_INFORMATION info; - DWORD exitcode; - - if (!in_tinytest_main) { - printf("\nERROR. On Windows, testcase_run_forked_ must be" - " called from within tinytest_main.\n"); - abort(); - } - if (opt_verbosity > 0) - printf("[forking] "); - - snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", commandname, - verbosity_flag, group->prefix, testcase->name); - - memset(&si, 0, sizeof(si)); - memset(&info, 0, sizeof(info)); - si.cb = sizeof(si); - - ok = CreateProcessA(commandname, buffer, NULL, NULL, 0, 0, NULL, NULL, &si, - &info); - if (!ok) { - printf("CreateProcess failed!\n"); - return 0; - } - WaitForSingleObject(info.hProcess, INFINITE); - GetExitCodeProcess(info.hProcess, &exitcode); - CloseHandle(info.hProcess); - CloseHandle(info.hThread); - if (exitcode == 0) - return OK; - else if (exitcode == MAGIC_EXITCODE) - return SKIP; - else - return FAIL; -#else - int outcome_pipe[2]; - pid_t pid; - (void)group; - - if (pipe(outcome_pipe)) - perror("opening pipe"); - - if (opt_verbosity > 0) - printf("[forking] "); - pid = fork(); -#ifdef FORK_BREAKS_GCOV - vproc_transaction_begin(0); -#endif - if (!pid) { - /* child. */ - int test_r, write_r; - char b[1]; - close(outcome_pipe[0]); - test_r = testcase_run_bare_(testcase); - assert(0 <= (int)test_r && (int)test_r <= 2); - b[0] = "NYS"[test_r]; - write_r = (int)write(outcome_pipe[1], b, 1); - if (write_r != 1) { - perror("write outcome to pipe"); - exit(1); - } - exit(0); - return FAIL; /* unreachable */ - } else { - /* parent */ - int status, r; - char b[1]; - /* Close this now, so that if the other side closes it, - * our read fails. */ - close(outcome_pipe[1]); - r = (int)read(outcome_pipe[0], b, 1); - if (r == 0) { - printf("[Lost connection!] "); - return FAIL; - } else if (r != 1) { - perror("read outcome from pipe"); - } - waitpid(pid, &status, 0); - close(outcome_pipe[0]); - return b[0] == 'Y' ? OK : (b[0] == 'S' ? SKIP : FAIL); - } -#endif -} - -#endif /* !NO_FORKING */ - -int testcase_run_one(const struct testgroup_t *group, - const struct testcase_t *testcase) { - enum outcome outcome; - - if (testcase->flags & (TT_SKIP | TT_OFF_BY_DEFAULT)) { - if (opt_verbosity > 0) - printf("%s%s: %s\n", group->prefix, testcase->name, - (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED"); - ++n_skipped; - return SKIP; - } - - if (opt_verbosity > 0 && !opt_forked) { - printf("%s%s: ", group->prefix, testcase->name); - } else { - if (opt_verbosity == 0) - printf("."); - cur_test_prefix = group->prefix; - cur_test_name = testcase->name; - } - -#ifndef NO_FORKING - if ((testcase->flags & TT_FORK) && !(opt_forked || opt_nofork)) { - outcome = testcase_run_forked_(group, testcase); - } else { -#else - { -#endif - outcome = testcase_run_bare_(testcase); - } - - if (outcome == OK) { - ++n_ok; - if (opt_verbosity > 0 && !opt_forked) - puts(opt_verbosity == 1 ? "OK" : ""); - } else if (outcome == SKIP) { - ++n_skipped; - if (opt_verbosity > 0 && !opt_forked) - puts("SKIPPED"); - } else { - ++n_bad; - if (!opt_forked) - printf("\n [%s FAILED]\n", testcase->name); - } - - if (opt_forked) { - exit(outcome == OK ? 0 : (outcome == SKIP ? MAGIC_EXITCODE : 1)); - return 1; /* unreachable */ - } else { - return (int)outcome; - } -} - -int tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, - unsigned long flag) { - int i, j; - size_t length = LONGEST_TEST_NAME; - char fullname[LONGEST_TEST_NAME]; - int found = 0; - if (strstr(arg, "..")) - length = strstr(arg, "..") - arg; - for (i = 0; groups[i].prefix; ++i) { - for (j = 0; groups[i].cases[j].name; ++j) { - struct testcase_t *testcase = &groups[i].cases[j]; - snprintf(fullname, sizeof(fullname), "%s%s", groups[i].prefix, - testcase->name); - if (!flag) { /* Hack! */ - printf(" %s", fullname); - if (testcase->flags & TT_OFF_BY_DEFAULT) - puts(" (Off by default)"); - else if (testcase->flags & TT_SKIP) - puts(" (DISABLED)"); - else - puts(""); - } - if (!strncmp(fullname, arg, length)) { - if (set) - testcase->flags |= flag; - else - testcase->flags &= ~flag; - ++found; - } - } - } - return found; -} - -static void usage(struct testgroup_t *groups, int list_groups) { - puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); - puts(" Specify tests by name, or using a prefix ending with '..'"); - puts(" To skip a test, prefix its name with a colon."); - puts(" To enable a disabled test, prefix its name with a plus."); - puts(" Use --list-tests for a list of tests."); - if (list_groups) { - puts("Known tests are:"); - tinytest_set_flag_(groups, "..", 1, 0); - } - exit(0); -} - -static int process_test_alias(struct testgroup_t *groups, const char *test) { - int i, j, n, r; - for (i = 0; cfg_aliases && cfg_aliases[i].name; ++i) { - if (!strcmp(cfg_aliases[i].name, test)) { - n = 0; - for (j = 0; cfg_aliases[i].tests[j]; ++j) { - r = process_test_option(groups, cfg_aliases[i].tests[j]); - if (r < 0) - return -1; - n += r; - } - return n; - } - } - printf("No such test alias as @%s!", test); - return -1; -} - -static int process_test_option(struct testgroup_t *groups, const char *test) { - int flag = TT_ENABLED_; - int n = 0; - if (test[0] == '@') { - return process_test_alias(groups, test + 1); - } else if (test[0] == ':') { - ++test; - flag = TT_SKIP; - } else if (test[0] == '+') { - ++test; - ++n; - if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) { - printf("No such test as %s!\n", test); - return -1; - } - } else { - ++n; - } - if (!tinytest_set_flag_(groups, test, 1, flag)) { - printf("No such test as %s!\n", test); - return -1; - } - return n; -} - -void tinytest_set_aliases(const struct testlist_alias_t *aliases) { - cfg_aliases = aliases; -} - -int tinytest_main(int c, const char **v, struct testgroup_t *groups) { - int i, j, n = 0; - -#ifdef _WIN32 - const char *sp = strrchr(v[0], '.'); - const char *extension = ""; - if (!sp || stricmp(sp, ".exe")) - extension = ".exe"; /* Add an exe so CreateProcess will work */ - snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension); - commandname[MAX_PATH] = '\0'; -#endif - for (i = 1; i < c; ++i) { - if (v[i][0] == '-') { - if (!strcmp(v[i], "--RUNNING-FORKED")) { - opt_forked = 1; - } else if (!strcmp(v[i], "--no-fork")) { - opt_nofork = 1; - } else if (!strcmp(v[i], "--quiet")) { - opt_verbosity = -1; - verbosity_flag = "--quiet"; - } else if (!strcmp(v[i], "--verbose")) { - opt_verbosity = 2; - verbosity_flag = "--verbose"; - } else if (!strcmp(v[i], "--terse")) { - opt_verbosity = 0; - verbosity_flag = "--terse"; - } else if (!strcmp(v[i], "--help")) { - usage(groups, 0); - } else if (!strcmp(v[i], "--list-tests")) { - usage(groups, 1); - } else { - printf("Unknown option %s. Try --help\n", v[i]); - return -1; - } - } else { - int r = process_test_option(groups, v[i]); - if (r < 0) - return -1; - n += r; - } - } - if (!n) - tinytest_set_flag_(groups, "..", 1, TT_ENABLED_); - -#ifdef _IONBF - setvbuf(stdout, NULL, _IONBF, 0); -#endif - - ++in_tinytest_main; - for (i = 0; groups[i].prefix; ++i) - for (j = 0; groups[i].cases[j].name; ++j) - if (groups[i].cases[j].flags & TT_ENABLED_) - testcase_run_one(&groups[i], &groups[i].cases[j]); - - --in_tinytest_main; - - if (opt_verbosity == 0) - puts(""); - - if (n_bad) - printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad, n_bad + n_ok, - n_skipped); - else if (opt_verbosity >= 1) - printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped); - - return (n_bad == 0) ? 0 : 1; -} - -int tinytest_get_verbosity_(void) { return opt_verbosity; } - -void tinytest_set_test_failed_(void) { - if (opt_verbosity <= 0 && cur_test_name) { - if (opt_verbosity == 0) - puts(""); - printf("%s%s: ", cur_test_prefix, cur_test_name); - cur_test_name = NULL; - } - cur_test_outcome = FAIL; -} - -void tinytest_set_test_skipped_(void) { - if (cur_test_outcome == OK) - cur_test_outcome = SKIP; -} - -char *tinytest_format_hex_(const void *val_, unsigned long len) { - const unsigned char *val = (unsigned char *)val_; - char *result, *cp; - size_t i; - - if (!val) - return strdup("null"); - if (!(result = (char *)malloc(len * 2 + 1))) - return strdup(""); - cp = result; - for (i = 0; i < len; ++i) { - *cp++ = "0123456789ABCDEF"[val[i] >> 4]; - *cp++ = "0123456789ABCDEF"[val[i] & 0x0f]; - } - *cp = 0; - return result; -} diff --git a/supportlibs/libsam3/src/ext/tinytest.h b/supportlibs/libsam3/src/ext/tinytest.h deleted file mode 100644 index bdddcbb54..000000000 --- a/supportlibs/libsam3/src/ext/tinytest.h +++ /dev/null @@ -1,104 +0,0 @@ -/* tinytest.h -- Copyright 2009-2012 Nick Mathewson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TINYTEST_H_INCLUDED_ -#define TINYTEST_H_INCLUDED_ - -/** Flag for a test that needs to run in a subprocess. */ -#define TT_FORK (1 << 0) -/** Runtime flag for a test we've decided to skip. */ -#define TT_SKIP (1 << 1) -/** Internal runtime flag for a test we've decided to run. */ -#define TT_ENABLED_ (1 << 2) -/** Flag for a test that's off by default. */ -#define TT_OFF_BY_DEFAULT (1 << 3) -/** If you add your own flags, make them start at this point. */ -#define TT_FIRST_USER_FLAG (1 << 4) - -typedef void (*testcase_fn)(void *); - -struct testcase_t; - -/** Functions to initialize/teardown a structure for a testcase. */ -struct testcase_setup_t { - /** Return a new structure for use by a given testcase. */ - void *(*setup_fn)(const struct testcase_t *); - /** Clean/free a structure from setup_fn. Return 1 if ok, 0 on err. */ - int (*cleanup_fn)(const struct testcase_t *, void *); -}; - -/** A single test-case that you can run. */ -struct testcase_t { - const char *name; /**< An identifier for this case. */ - testcase_fn fn; /**< The function to run to implement this case. */ - unsigned long flags; /**< Bitfield of TT_* flags. */ - const struct testcase_setup_t *setup; /**< Optional setup/cleanup fns*/ - void *setup_data; /**< Extra data usable by setup function */ -}; -#define END_OF_TESTCASES \ - { NULL, NULL, 0, NULL, NULL } - -/** A group of tests that are selectable together. */ -struct testgroup_t { - const char *prefix; /**< Prefix to prepend to testnames. */ - struct testcase_t *cases; /** Array, ending with END_OF_TESTCASES */ -}; -#define END_OF_GROUPS \ - { NULL, NULL } - -struct testlist_alias_t { - const char *name; - const char **tests; -}; -#define END_OF_ALIASES \ - { NULL, NULL } - -/** Implementation: called from a test to indicate failure, before logging. */ -void tinytest_set_test_failed_(void); -/** Implementation: called from a test to indicate that we're skipping. */ -void tinytest_set_test_skipped_(void); -/** Implementation: return 0 for quiet, 1 for normal, 2 for loud. */ -int tinytest_get_verbosity_(void); -/** Implementation: Set a flag on tests matching a name; returns number - * of tests that matched. */ -int tinytest_set_flag_(struct testgroup_t *, const char *, int set, - unsigned long); -/** Implementation: Put a chunk of memory into hex. */ -char *tinytest_format_hex_(const void *, unsigned long); - -/** Set all tests in 'groups' matching the name 'named' to be skipped. */ -#define tinytest_skip(groups, named) \ - tinytest_set_flag_(groups, named, 1, TT_SKIP) - -/** Run a single testcase in a single group. */ -int testcase_run_one(const struct testgroup_t *, const struct testcase_t *); - -void tinytest_set_aliases(const struct testlist_alias_t *aliases); - -/** Run a set of testcases from an END_OF_GROUPS-terminated array of groups, - as selected from the command line. */ -int tinytest_main(int argc, const char **argv, struct testgroup_t *groups); - -#endif diff --git a/supportlibs/libsam3/src/ext/tinytest_macros.h b/supportlibs/libsam3/src/ext/tinytest_macros.h deleted file mode 100644 index 817734020..000000000 --- a/supportlibs/libsam3/src/ext/tinytest_macros.h +++ /dev/null @@ -1,219 +0,0 @@ -/* tinytest_macros.h -- Copyright 2009-2012 Nick Mathewson - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TINYTEST_MACROS_H_INCLUDED_ -#define TINYTEST_MACROS_H_INCLUDED_ - -/* Helpers for defining statement-like macros */ -#define TT_STMT_BEGIN do { -#define TT_STMT_END \ - } \ - while (0) - -/* Redefine this if your test functions want to abort with something besides - * "goto end;" */ -#ifndef TT_EXIT_TEST_FUNCTION -#define TT_EXIT_TEST_FUNCTION \ - TT_STMT_BEGIN goto end; \ - TT_STMT_END -#endif - -/* Redefine this if you want to note success/failure in some different way. */ -#ifndef TT_DECLARE -#define TT_DECLARE(prefix, args) \ - TT_STMT_BEGIN \ - printf("\n %s %s:%d: ", prefix, __FILE__, __LINE__); \ - printf args; \ - TT_STMT_END -#endif - -/* Announce a failure. Args are parenthesized printf args. */ -#define TT_GRIPE(args) TT_DECLARE("FAIL", args) - -/* Announce a non-failure if we're verbose. */ -#define TT_BLATHER(args) \ - TT_STMT_BEGIN \ - if (tinytest_get_verbosity_() > 1) \ - TT_DECLARE(" OK", args); \ - TT_STMT_END - -#define TT_DIE(args) \ - TT_STMT_BEGIN \ - tinytest_set_test_failed_(); \ - TT_GRIPE(args); \ - TT_EXIT_TEST_FUNCTION; \ - TT_STMT_END - -#define TT_FAIL(args) \ - TT_STMT_BEGIN \ - tinytest_set_test_failed_(); \ - TT_GRIPE(args); \ - TT_STMT_END - -/* Fail and abort the current test for the reason in msg */ -#define tt_abort_printf(msg) TT_DIE(msg) -#define tt_abort_perror(op) \ - TT_DIE(("%s: %s [%d]", (op), strerror(errno), errno)) -#define tt_abort_msg(msg) TT_DIE(("%s", msg)) -#define tt_abort() TT_DIE(("%s", "(Failed.)")) - -/* Fail but do not abort the current test for the reason in msg. */ -#define tt_fail_printf(msg) TT_FAIL(msg) -#define tt_fail_perror(op) \ - TT_FAIL(("%s: %s [%d]", (op), strerror(errno), errno)) -#define tt_fail_msg(msg) TT_FAIL(("%s", msg)) -#define tt_fail() TT_FAIL(("%s", "(Failed.)")) - -/* End the current test, and indicate we are skipping it. */ -#define tt_skip() \ - TT_STMT_BEGIN \ - tinytest_set_test_skipped_(); \ - TT_EXIT_TEST_FUNCTION; \ - TT_STMT_END - -#define tt_want_(b, msg, fail) \ - TT_STMT_BEGIN \ - if (!(b)) { \ - tinytest_set_test_failed_(); \ - TT_GRIPE(("%s", msg)); \ - fail; \ - } else { \ - TT_BLATHER(("%s", msg)); \ - } \ - TT_STMT_END - -/* Assert b, but do not stop the test if b fails. Log msg on failure. */ -#define tt_want_msg(b, msg) tt_want_(b, msg, ); - -/* Assert b and stop the test if b fails. Log msg on failure. */ -#define tt_assert_msg(b, msg) tt_want_(b, msg, TT_EXIT_TEST_FUNCTION); - -/* Assert b, but do not stop the test if b fails. */ -#define tt_want(b) tt_want_msg((b), "want(" #b ")") -/* Assert b, and stop the test if b fails. */ -#define tt_assert(b) tt_assert_msg((b), "assert(" #b ")") - -#define tt_assert_test_fmt_type(a, b, str_test, type, test, printf_type, \ - printf_fmt, setup_block, cleanup_block, \ - die_on_fail) \ - TT_STMT_BEGIN \ - type val1_ = (a); \ - type val2_ = (b); \ - int tt_status_ = (test); \ - if (!tt_status_ || tinytest_get_verbosity_() > 1) { \ - printf_type print_; \ - printf_type print1_; \ - printf_type print2_; \ - type value_ = val1_; \ - setup_block; \ - print1_ = print_; \ - value_ = val2_; \ - setup_block; \ - print2_ = print_; \ - TT_DECLARE(tt_status_ ? " OK" : "FAIL", \ - ("assert(%s): " printf_fmt " vs " printf_fmt, str_test, \ - print1_, print2_)); \ - print_ = print1_; \ - cleanup_block; \ - print_ = print2_; \ - cleanup_block; \ - if (!tt_status_) { \ - tinytest_set_test_failed_(); \ - die_on_fail; \ - } \ - } \ - TT_STMT_END - -#define tt_assert_test_type(a, b, str_test, type, test, fmt, die_on_fail) \ - tt_assert_test_fmt_type(a, b, str_test, type, test, type, fmt, \ - { print_ = value_; }, {}, die_on_fail) - -#define tt_assert_test_type_opt(a, b, str_test, type, test, fmt, die_on_fail) \ - tt_assert_test_fmt_type(a, b, str_test, type, test, type, fmt, \ - { print_ = value_ ? value_ : ""; }, {}, \ - die_on_fail) - -/* Helper: assert that a op b, when cast to type. Format the values with - * printf format fmt on failure. */ -#define tt_assert_op_type(a, op, b, type, fmt) \ - tt_assert_test_type(a, b, #a " " #op " " #b, type, (val1_ op val2_), fmt, \ - TT_EXIT_TEST_FUNCTION) - -#define tt_int_op(a, op, b) \ - tt_assert_test_type(a, b, #a " " #op " " #b, long, (val1_ op val2_), "%ld", \ - TT_EXIT_TEST_FUNCTION) - -#define tt_uint_op(a, op, b) \ - tt_assert_test_type(a, b, #a " " #op " " #b, unsigned long, \ - (val1_ op val2_), "%lu", TT_EXIT_TEST_FUNCTION) - -#define tt_ptr_op(a, op, b) \ - tt_assert_test_type(a, b, #a " " #op " " #b, const void *, (val1_ op val2_), \ - "%p", TT_EXIT_TEST_FUNCTION) - -#define tt_str_op(a, op, b) \ - tt_assert_test_type_opt(a, b, #a " " #op " " #b, const char *, \ - (val1_ && val2_ && strcmp(val1_, val2_) op 0), \ - "<%s>", TT_EXIT_TEST_FUNCTION) - -#define tt_mem_op(expr1, op, expr2, len) \ - tt_assert_test_fmt_type( \ - expr1, expr2, #expr1 " " #op " " #expr2, const void *, \ - (val1_ && val2_ && memcmp(val1_, val2_, len) op 0), char *, "%s", \ - { print_ = tinytest_format_hex_(value_, (len)); }, \ - { \ - if (print_) \ - free(print_); \ - }, \ - TT_EXIT_TEST_FUNCTION); - -#define tt_want_int_op(a, op, b) \ - tt_assert_test_type(a, b, #a " " #op " " #b, long, (val1_ op val2_), "%ld", \ - (void)0) - -#define tt_want_uint_op(a, op, b) \ - tt_assert_test_type(a, b, #a " " #op " " #b, unsigned long, \ - (val1_ op val2_), "%lu", (void)0) - -#define tt_want_ptr_op(a, op, b) \ - tt_assert_test_type(a, b, #a " " #op " " #b, const void *, (val1_ op val2_), \ - "%p", (void)0) - -#define tt_want_str_op(a, op, b) \ - tt_assert_test_type(a, b, #a " " #op " " #b, const char *, \ - (strcmp(val1_, val2_) op 0), "<%s>", (void)0) - -#define tt_want_mem_op(expr1, op, expr2, len) \ - tt_assert_test_fmt_type( \ - expr1, expr2, #expr1 " " #op " " #expr2, const void *, \ - (val1_ && val2_ && memcmp(val1_, val2_, len) op 0), char *, "%s", \ - { print_ = tinytest_format_hex_(value_, (len)); }, \ - { \ - if (print_) \ - free(print_); \ - }, \ - (void)0); - -#endif diff --git a/supportlibs/libsam3/src/libsam3/libsam3.c b/supportlibs/libsam3/src/libsam3/libsam3.c deleted file mode 100644 index 5a645806f..000000000 --- a/supportlibs/libsam3/src/libsam3/libsam3.c +++ /dev/null @@ -1,1345 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include "libsam3.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __MINGW32__ -//#include -#include -#include -#include -#ifndef MSG_NOSIGNAL -#define MSG_NOSIGNAL 0 -#endif -#ifndef SHUT_RDWR -#define SHUT_RDWR 2 -#endif -#endif - -#ifdef __unix__ -#include -#include -#include -#include -#include -#include -#endif -//////////////////////////////////////////////////////////////////////////////// -int libsam3_debug = 0; - -//////////////////////////////////////////////////////////////////////////////// -/* convert struct timeval to milliseconds */ -/* -static inline uint64_t timeval2ms (const struct timeval *tv) { - return ((uint64_t)tv->tv_sec)*1000+((uint64_t)tv->tv_usec)/1000; -} -*/ - -/* convert milliseconds to timeval struct */ -static inline void ms2timeval(struct timeval *tv, uint64_t ms) { - tv->tv_sec = ms / 1000; - tv->tv_usec = (ms % 1000) * 1000; -} - -int sam3tcpSetTimeoutSend(int fd, int timeoutms) { - if (fd >= 0 && timeoutms >= 0) { - struct timeval tv; - // - ms2timeval(&tv, timeoutms); - return (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 ? -1 - : 0); - } - return -1; -} - -int sam3tcpSetTimeoutReceive(int fd, int timeoutms) { - if (fd >= 0 && timeoutms >= 0) { - struct timeval tv; - // - ms2timeval(&tv, timeoutms); - return (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ? -1 - : 0); - } - return -1; -} - -int sam3CheckValidKeyLength(const char *pubkey) { - if (strlen(pubkey) >= SAM3_PUBKEY_SIZE && - strlen(pubkey) <= SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE) { - return 1; - } - return 0; -} - -int sam3tcpConnectIP(uint32_t ip, int port) { - struct sockaddr_in addr; - int fd, val = 1; - char ipstr[18]; - // - if (ip == 0 || ip == 0xffffffffUL || port < 1 || port > 65535) - return -1; - // - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - if (libsam3_debug) - fprintf(stderr, "ERROR: can't create socket\n"); - return -1; - } - // - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = ip; - // - ipstr[0] = 0; - if (libsam3_debug) { - if (getnameinfo((struct sockaddr *)&addr, sizeof(struct sockaddr_in), ipstr, - sizeof(ipstr), NULL, 0, NI_NUMERICHOST) == 0) { - fprintf(stderr, "connecting to [%s:%d]...\n", ipstr, port); - } - } - // - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); - // - if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { - if (libsam3_debug) - fprintf(stderr, "ERROR: can't connect\n"); - close(fd); - return -1; - } - // - if (libsam3_debug && ipstr[0]) - fprintf(stderr, "connected to [%s:%d]\n", ipstr, port); - // - return fd; -} - -/* returns fd or -1 */ -int sam3tcpConnect(const char *hostname, int port, uint32_t *ip) { - struct hostent *host = NULL; - // - if (hostname == NULL || !hostname[0] || port < 1 || port > 65535) - return -1; - // - host = gethostbyname(hostname); - if (host == NULL || host->h_name == NULL || !host->h_name[0]) { - if (libsam3_debug) - fprintf(stderr, "ERROR: can't resolve '%s'\n", hostname); - return -1; - } - // - if (libsam3_debug) { - char ipstr[18]; - // - struct sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr = *((struct in_addr *)host->h_addr); - // - if (getnameinfo((struct sockaddr *)&addr, sizeof(struct sockaddr_in), ipstr, - sizeof(ipstr), NULL, 0, NI_NUMERICHOST) == 0) { - fprintf(stderr, "resolving: %s is [%s]...\n", hostname, ipstr); - } - } - // - if (ip != NULL) - *ip = ((struct in_addr *)host->h_addr)->s_addr; - return sam3tcpConnectIP(((struct in_addr *)host->h_addr)->s_addr, port); -} - -// <0: error; 0: ok -int sam3tcpDisconnect(int fd) { - if (fd >= 0) { - shutdown(fd, SHUT_RDWR); - return close(fd); - } - // - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -// <0: error; 0: ok -int sam3tcpSend(int fd, const void *buf, size_t bufSize) { - const char *c = (const char *)buf; - // - if (fd < 0 || (buf == NULL && bufSize > 0)) - return -1; - // - while (bufSize > 0) { - int wr = send(fd, c, bufSize, MSG_NOSIGNAL); - // - if (wr < 0 && errno == EINTR) - continue; // interrupted by signal - if (wr <= 0) - return -1; // either error or - c += wr; - bufSize -= wr; - } - // - return 0; -} - -/* <0: received (-res) bytes; read error */ -/* can return less that requesten bytes even if `allowPartial` is 0 when - * connection is closed */ -ssize_t sam3tcpReceiveEx(int fd, void *buf, size_t bufSize, int allowPartial) { - char *c = (char *)buf; - ssize_t total = 0; - // - if (fd < 0 || (buf == NULL && bufSize > 0)) - return -1; - // - while (bufSize > 0) { - int rd = recv(fd, c, bufSize, 0); - // - if (rd < 0 && errno == EINTR) - continue; // interrupted by signal - if (rd == 0) - return total; - if (rd < 0) - return -total; - c += rd; - total += rd; - bufSize -= rd; - if (allowPartial) - break; - } - // - return total; -} - -ssize_t sam3tcpReceive(int fd, void *buf, size_t bufSize) { - return sam3tcpReceiveEx(fd, buf, bufSize, 0); -} - -//////////////////////////////////////////////////////////////////////////////// -__attribute__((format(printf, 2, 3))) int sam3tcpPrintf(int fd, const char *fmt, - ...) { - int res; - char buf[1024], *p = buf; - int size = sizeof(buf) - 1; - // - for (;;) { - va_list ap; - char *np; - int n; - // - va_start(ap, fmt); - n = vsnprintf(p, size, fmt, ap); - va_end(ap); - // - if (n > -1 && n < size) - break; - if (n > -1) - size = n + 1; - else - size *= 2; - if (p == buf) { - if ((p = malloc(size + 4)) == NULL) - return -1; - } else { - if ((np = realloc(p, size + 4)) == NULL) { - free(p); - return -1; - } - p = np; - } - } - // - if (libsam3_debug) - fprintf(stderr, "SENDING: %s", p); - res = sam3tcpSend(fd, p, strlen(p)); - if (p != buf) - free(p); - return res; -} - -int sam3tcpReceiveStr(int fd, char *dest, size_t maxSize) { - char *d = dest; - // - if (maxSize < 1 || fd < 0 || dest == NULL) - return -1; - memset(dest, 0, maxSize); - while (maxSize > 1) { - char *e; - int rd = recv(fd, d, maxSize - 1, MSG_PEEK); - // - if (rd < 0 && errno == EINTR) - continue; // interrupted by signal - if (rd == 0) { - rd = recv(fd, d, 1, 0); - if (rd < 0 && errno == EINTR) - continue; // interrupted by signal - if (d[0] == '\n') { - d[0] = 0; // remove '\n' - return 0; - } - } else { - if (rd < 0) - return -1; // error or connection closed; alas - } - // check for EOL - d[maxSize - 1] = 0; - if ((e = strchr(d, '\n')) != NULL) { - rd = e - d + 1; // bytes to receive - if (sam3tcpReceive(fd, d, rd) < 0) - return -1; // alas - d[rd - 1] = 0; // remove '\n' - return 0; // done - } else { - // let's receive this part and go on - if (sam3tcpReceive(fd, d, rd) < 0) - return -1; // alas - maxSize -= rd; - d += rd; - } - } - // alas, the string is too big - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3udpSendToIP(uint32_t ip, int port, const void *buf, size_t bufSize) { - // TODO: ipv6 - struct sockaddr_in addr; - int fd, res; - // - if (buf == NULL || bufSize < 1) - return -1; - if (port < 1 || port > 65535) - port = 7655; - // - if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - if (libsam3_debug) - fprintf(stderr, "ERROR: can't create socket\n"); - return -1; - } - // - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = ip; - // - res = sendto(fd, buf, bufSize, 0, (struct sockaddr *)&addr, sizeof(addr)); - // - if (res < 0) { - if (libsam3_debug) { - res = errno; - fprintf(stderr, "UDP ERROR (%d): %s\n", res, strerror(res)); - } - res = -1; - } else { - if (libsam3_debug) - fprintf(stderr, "UDP: %d bytes sent\n", res); - } - // - close(fd); - // - return (res >= 0 ? 0 : -1); -} - -int sam3udpSendTo(const char *hostname, int port, const void *buf, - size_t bufSize, uint32_t *ip) { - struct hostent *host = NULL; - // TODO: ipv6 - if (buf == NULL || bufSize < 1) - return -1; - if (hostname == NULL || !hostname[0]) - hostname = "localhost"; - if (port < 1 || port > 65535) - port = 7655; - // - host = gethostbyname(hostname); - if (host == NULL || host->h_name == NULL || !host->h_name[0]) { - if (libsam3_debug) - fprintf(stderr, "ERROR: can't resolve '%s'\n", hostname); - return -1; - } - // - if (ip != NULL) - *ip = ((struct in_addr *)host->h_addr)->s_addr; - return sam3udpSendToIP(((struct in_addr *)host->h_addr)->s_addr, port, buf, - bufSize); -} - -//////////////////////////////////////////////////////////////////////////////// -void sam3FreeFieldList(SAMFieldList *list) { - while (list != NULL) { - SAMFieldList *c = list; - // - list = list->next; - if (c->name != NULL) - free(c->name); - if (c->value != NULL) - free(c->value); - free(c); - } -} - -void sam3DumpFieldList(const SAMFieldList *list) { - for (; list != NULL; list = list->next) { - fprintf(stderr, "%s=[%s]\n", list->name, list->value); - } -} - -const char *sam3FindField(const SAMFieldList *list, const char *field) { - if (list != NULL && field != NULL) { - for (list = list->next; list != NULL; list = list->next) { - if (list->name != NULL && strcmp(field, list->name) == 0) - return list->value; - } - } - return NULL; -} - -static char *xstrdup(const char *s, int len) { - if (len >= 0) { - char *res = malloc(len + 1); - // - if (res != NULL) { - if (len > 0) - memcpy(res, s, len); - res[len] = 0; - } - // - return res; - } - // - return NULL; -} - -// returns NULL if there are no more tokens -static inline const char *xstrtokend(const char *s) { - while (*s && isspace(*s)) - ++s; - // - if (*s) { - char qch = 0; - // - while (*s) { - if (*s == qch) { - qch = 0; - } else if (*s == '"') { - qch = *s; - } else if (qch) { - if (*s == '\\' && s[1]) - ++s; - } else if (isspace(*s)) { - break; - } - ++s; - } - } else { - s = NULL; - } - // - return s; -} - -SAMFieldList *sam3ParseReply(const char *rep) { - SAMFieldList *first = NULL, *last, *c; - const char *p = rep, *e, *e1; - // - // first 2 words - while (*p && isspace(*p)) - ++p; - if ((e = xstrtokend(p)) == NULL) - return NULL; - if ((e1 = xstrtokend(e)) == NULL) - return NULL; - // - if ((first = last = c = malloc(sizeof(SAMFieldList))) == NULL) - return NULL; - c->next = NULL; - c->name = c->value = NULL; - if ((c->name = xstrdup(p, e - p)) == NULL) - goto error; - while (*e && isspace(*e)) - ++e; - if ((c->value = xstrdup(e, e1 - e)) == NULL) - goto error; - // - p = e1; - while (*p) { - while (*p && isspace(*p)) - ++p; - if ((e = xstrtokend(p)) == NULL) - break; // no more tokens - // - if (libsam3_debug) - fprintf(stderr, "<%s>\n", p); - // - if ((c = malloc(sizeof(SAMFieldList))) == NULL) - return NULL; - c->next = NULL; - c->name = c->value = NULL; - last->next = c; - last = c; - // - if ((e1 = memchr(p, '=', e - p)) != NULL) { - // key=value - if ((c->name = xstrdup(p, e1 - p)) == NULL) - goto error; - if ((c->value = xstrdup(e1 + 1, e - e1 - 1)) == NULL) - goto error; - } else { - // only key (there is no such replies in SAMv3, but... - if ((c->name = xstrdup(p, e - p)) == NULL) - goto error; - if ((c->value = strdup("")) == NULL) - goto error; - } - p = e; - } - // - if (libsam3_debug) - sam3DumpFieldList(first); - // - return first; -error: - sam3FreeFieldList(first); - return NULL; -} - -// NULL: error; else: list of fields -// first item is always 2-word reply, with first word in name and second in -// value -SAMFieldList *sam3ReadReply(int fd) { - char rep[2048]; // should be enough for any reply - // - if (sam3tcpReceiveStr(fd, rep, sizeof(rep)) < 0) - return NULL; - if (libsam3_debug) - fprintf(stderr, "SAM REPLY: [%s]\n", rep); - return sam3ParseReply(rep); -} - -// example: -// r0: 'HELLO' -// r1: 'REPLY' -// field: NULL or 'RESULT' -// VALUE: NULL or 'OK' -// returns bool -int sam3IsGoodReply(const SAMFieldList *list, const char *r0, const char *r1, - const char *field, const char *value) { - if (list != NULL && list->name != NULL && list->value != NULL) { - if (r0 != NULL && strcmp(r0, list->name) != 0) - return 0; - if (r1 != NULL && strcmp(r1, list->value) != 0) - return 0; - if (field != NULL) { - for (list = list->next; list != NULL; list = list->next) { - if (list->name == NULL || list->value == NULL) - return 0; // invalid list, heh - if (strcmp(field, list->name) == 0) { - if (value != NULL && strcmp(value, list->value) != 0) - return 0; - return 1; - } - } - } - return 1; - } - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// by Bob Jenkins -// public domain -// http://burtleburtle.net/bob/rand/smallprng.html -// -//////////////////////////////////////////////////////////////////////////////// -typedef struct { - uint32_t a, b, c, d; -} BJRandCtx; - -#define BJPRNG_ROT(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) - -static uint32_t bjprngRand(BJRandCtx *x) { - uint32_t e; - /* original: - e = x->a-BJPRNG_ROT(x->b, 27); - x->a = x->b^BJPRNG_ROT(x->c, 17); - x->b = x->c+x->d; - x->c = x->d+e; - x->d = e+x->a; - */ - /* better, but slower at least in idiotic m$vc */ - e = x->a - BJPRNG_ROT(x->b, 23); - x->a = x->b ^ BJPRNG_ROT(x->c, 16); - x->b = x->c + BJPRNG_ROT(x->d, 11); - x->c = x->d + e; - x->d = e + x->a; - // - return x->d; -} - -static void bjprngInit(BJRandCtx *x, uint32_t seed) { - x->a = 0xf1ea5eed; - x->b = x->c = x->d = seed; - for (int i = 0; i < 20; ++i) - bjprngRand(x); -} - -static inline uint32_t hashint(uint32_t a) { - a -= (a << 6); - a ^= (a >> 17); - a -= (a << 9); - a ^= (a << 4); - a -= (a << 3); - a ^= (a << 10); - a ^= (a >> 15); - return a; -} - -static uint32_t genSeed(void) { - uint32_t res; -#ifndef WIN32 - struct sysinfo sy; - pid_t pid = getpid(); - // - sysinfo(&sy); - res = hashint((uint32_t)pid) ^ hashint((uint32_t)time(NULL)) ^ - hashint((uint32_t)sy.sharedram) ^ hashint((uint32_t)sy.bufferram) ^ - hashint((uint32_t)sy.uptime); -#else - res = hashint((uint32_t)GetCurrentProcessId()) ^ - hashint((uint32_t)GetTickCount()); -#endif - return hashint(res); -} - -//////////////////////////////////////////////////////////////////////////////// -size_t sam3GenChannelName(char *dest, size_t minlen, size_t maxlen) { - BJRandCtx rc; - size_t len; - size_t retlen; - // - if (dest == NULL || minlen < 1 || maxlen < minlen || minlen > 65536 || - maxlen > 65536) - return -1; - bjprngInit(&rc, genSeed()); - len = minlen + (bjprngRand(&rc) % (maxlen - minlen + 1)); - retlen = len; - while (len--) { - int ch = bjprngRand(&rc) % 64; - // - if (ch >= 0 && ch < 10) - ch += '0'; - else if (ch >= 10 && ch < 36) - ch += 'A' - 10; - else if (ch >= 36 && ch < 62) - ch += 'a' - 36; - else if (ch == 62) - ch = '-'; - else if (ch == 63) - ch = '_'; - else if (ch > 64) - abort(); - *dest++ = ch; - } - *dest++ = 0; - return retlen; -} - -//////////////////////////////////////////////////////////////////////////////// -static int sam3HandshakeInternal(int fd) { - SAMFieldList *rep = NULL; - // - if (sam3tcpPrintf(fd, "HELLO VERSION MIN=3.0 MAX=3.1\n") < 0) - goto error; - rep = sam3ReadReply(fd); - if (!sam3IsGoodReply(rep, "HELLO", "REPLY", "RESULT", "OK")) - goto error; - sam3FreeFieldList(rep); - return fd; -error: - sam3tcpDisconnect(fd); - if (rep != NULL) - sam3FreeFieldList(rep); - return -1; -} - -int sam3HandshakeIP(uint32_t ip, int port) { - int fd; - // - if ((fd = sam3tcpConnectIP(ip, (port < 1 || port > 65535 ? 7656 : port))) < 0) - return -1; - return sam3HandshakeInternal(fd); -} - -int sam3Handshake(const char *hostname, int port, uint32_t *ip) { - int fd; - // - if ((fd = sam3tcpConnect( - (hostname == NULL || !hostname[0] ? "localhost" : hostname), - (port < 1 || port > 65535 ? 7656 : port), ip)) < 0) - return -1; - return sam3HandshakeInternal(fd); -} - -//////////////////////////////////////////////////////////////////////////////// -static inline void strcpyerr(Sam3Session *ses, const char *errstr) { - memset(ses->error, 0, sizeof(ses->error)); - if (errstr != NULL) - strncpy(ses->error, errstr, sizeof(ses->error) - 1); -} - -int sam3GenerateKeys(Sam3Session *ses, const char *hostname, int port, - int sigType) { - if (ses != NULL) { - SAMFieldList *rep = NULL; - int fd, res = -1; - static const char *sigtypes[5] = { - "SIGNATURE_TYPE=DSA_SHA1", "SIGNATURE_TYPE=ECDSA_SHA256_P256", - "SIGNATURE_TYPE=ECDSA_SHA384_P384", "SIGNATURE_TYPE=ECDSA_SHA512_P521", - "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"}; - // - if ((fd = sam3Handshake(hostname, port, NULL)) < 0) { - strcpyerr(ses, "I2P_ERROR"); - return -1; - } - // - if (sam3tcpPrintf(fd, "DEST GENERATE %s\n", sigtypes[sigType]) >= 0) { - if ((rep = sam3ReadReply(fd)) != NULL && - sam3IsGoodReply(rep, "DEST", "REPLY", NULL, NULL)) { - const char *pub = sam3FindField(rep, "PUB"), - *priv = sam3FindField(rep, "PRIV"); - // - if (pub != NULL && sam3CheckValidKeyLength(pub) && priv != NULL && - strlen(priv) >= SAM3_PRIVKEY_MIN_SIZE) { - strcpy(ses->pubkey, pub); - strcpy(ses->privkey, priv); - res = 0; - } - } - } - // - sam3FreeFieldList(rep); - sam3tcpDisconnect(fd); - // - return res; - } - return -1; -} - -int sam3NameLookup(Sam3Session *ses, const char *hostname, int port, - const char *name) { - if (ses != NULL && name != NULL && name[0]) { - SAMFieldList *rep = NULL; - int fd, res = -1; - // - if ((fd = sam3Handshake(hostname, port, NULL)) < 0) { - strcpyerr(ses, "I2P_ERROR"); - return -1; - } - // - strcpyerr(ses, "I2P_ERROR"); - if (sam3tcpPrintf(fd, "NAMING LOOKUP NAME=%s\n", name) >= 0) { - if ((rep = sam3ReadReply(fd)) != NULL && - sam3IsGoodReply(rep, "NAMING", "REPLY", "RESULT", NULL)) { - const char *rs = sam3FindField(rep, "RESULT"), - *pub = sam3FindField(rep, "VALUE"); - // - if (strcmp(rs, "OK") == 0) { - if (pub != NULL && sam3CheckValidKeyLength(pub)) { - strcpy(ses->destkey, pub); - strcpyerr(ses, NULL); - res = 0; - } - } else if (rs[0]) { - strcpyerr(ses, rs); - } - } - } - // - sam3FreeFieldList(rep); - sam3tcpDisconnect(fd); - // - return res; - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -static int sam3CloseConnectionInternal(Sam3Connection *conn) { - return (conn->fd >= 0 ? sam3tcpDisconnect(conn->fd) : 0); -} - -int sam3CloseConnection(Sam3Connection *conn) { - if (conn != NULL) { - int res = sam3CloseConnectionInternal(conn); - // - if (conn->ses != NULL) { - for (Sam3Connection *p = NULL, *c = conn->ses->connlist; c != NULL; - p = c, c = c->next) { - if (c == conn) { - if (p == NULL) - conn->ses->connlist = c->next; - else - p->next = c->next; - break; - } - } - } - free(conn); - // - return res; - } - return -1; -} - -int sam3CloseSession(Sam3Session *ses) { - if (ses != NULL) { - for (Sam3Connection *n, *c = ses->connlist; c != NULL; c = n) { - n = c->next; - sam3CloseConnectionInternal(c); - free(c); - } - if (ses->fwd_fd >= 0) - sam3tcpDisconnect(ses->fwd_fd); - if (ses->fd >= 0) - sam3tcpDisconnect(ses->fd); - memset(ses, 0, sizeof(Sam3Session)); - ses->fd = -1; - return 0; - } - return -1; -} - -int sam3CreateSilentSession(Sam3Session *ses, const char *hostname, int port, - const char *privkey, Sam3SessionType type, - Sam3SigType sigType, const char *params) { - int r = - sam3CreateSession(ses, hostname, port, privkey, type, sigType, params); - if (r != 0) { - return r; - } - ses->silent = true; - return 0; -} - -int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, - const char *privkey, Sam3SessionType type, - Sam3SigType sigType, const char *params) { - if (ses != NULL) { - static const char *typenames[3] = {"RAW", "DATAGRAM", "STREAM"}; - static const char *sigtypes[5] = { - "SIGNATURE_TYPE=DSA_SHA1", "SIGNATURE_TYPE=ECDSA_SHA256_P256", - "SIGNATURE_TYPE=ECDSA_SHA384_P384", "SIGNATURE_TYPE=ECDSA_SHA512_P521", - "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519"}; - - SAMFieldList *rep; - const char *v = NULL; - const char *pdel = (params != NULL ? " " : ""); - // - memset(ses, 0, sizeof(Sam3Session)); - ses->fd = -1; - ses->fwd_fd = -1; - ses->silent = false; - // - if (privkey != NULL && strlen(privkey) < SAM3_PRIVKEY_MIN_SIZE) - goto error; - if ((int)type < 0 || (int)type > 2) - goto error; - if (privkey == NULL) - privkey = "TRANSIENT"; - // - ses->type = type; - ses->sigType = sigType; - ses->port = (type == SAM3_SESSION_STREAM ? (port ? port : 7656) : 7655); - sam3GenChannelName(ses->channel, 32, 64); - if (libsam3_debug) - fprintf(stderr, "sam3CreateSession: channel=[%s]\n", ses->channel); - // - if ((ses->fd = sam3Handshake(hostname, port, &ses->ip)) < 0) - goto error; - // - if (libsam3_debug) - fprintf(stderr, "sam3CreateSession: creating session (%s)...\n", - typenames[(int)type]); - if (sam3tcpPrintf( - ses->fd, "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s %s %s %s\n", - typenames[(int)type], ses->channel, privkey, sigtypes[(int)sigType], - pdel, (params != NULL ? params : "")) < 0) - goto error; - if ((rep = sam3ReadReply(ses->fd)) == NULL) - goto error; - if (!sam3IsGoodReply(rep, "SESSION", "STATUS", "RESULT", "OK") || - (v = sam3FindField(rep, "DESTINATION")) == NULL || - strlen(v) < SAM3_PRIVKEY_MIN_SIZE) { - if (libsam3_debug) - fprintf(stderr, "sam3CreateSession: invalid reply (%ld)...\n", - (v != NULL ? strlen(v) : -1)); - if (libsam3_debug) - sam3DumpFieldList(rep); - sam3FreeFieldList(rep); - goto error; - } - // save our keys - strcpy(ses->privkey, v); - sam3FreeFieldList(rep); - // get public key - if (sam3tcpPrintf(ses->fd, "NAMING LOOKUP NAME=ME\n") < 0) - goto error; - if ((rep = sam3ReadReply(ses->fd)) == NULL) - goto error; - v = NULL; - if (!sam3IsGoodReply(rep, "NAMING", "REPLY", "RESULT", "OK") || - (v = sam3FindField(rep, "VALUE")) == NULL || - !sam3CheckValidKeyLength(v)) { - if (libsam3_debug) - fprintf(stderr, "sam3CreateSession: invalid NAMING reply (%ld)...\n", - (v != NULL ? strlen(v) : -1)); - if (libsam3_debug) - sam3DumpFieldList(rep); - sam3FreeFieldList(rep); - goto error; - } - strcpy(ses->pubkey, v); - sam3FreeFieldList(rep); - // - if (libsam3_debug) - fprintf(stderr, "sam3CreateSession: complete.\n"); - return 0; - } -error: - sam3CloseSession(ses); - return -1; -} - -Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey) { - if (ses != NULL) { - SAMFieldList *rep; - Sam3Connection *conn; - // - if (ses->type != SAM3_SESSION_STREAM) { - strcpyerr(ses, "INVALID_SESSION_TYPE"); - return NULL; - } - if (ses->fd < 0) { - strcpyerr(ses, "INVALID_SESSION"); - return NULL; - } - if (destkey == NULL || !sam3CheckValidKeyLength(destkey)) { - strcpyerr(ses, "INVALID_KEY"); - return NULL; - } - if ((conn = calloc(1, sizeof(Sam3Connection))) == NULL) { - strcpyerr(ses, "NO_MEMORY"); - return NULL; - } - if ((conn->fd = sam3HandshakeIP(ses->ip, ses->port)) < 0) { - strcpyerr(ses, "IO_ERROR_SK"); - goto error; - } - if (sam3tcpPrintf(conn->fd, - "STREAM CONNECT ID=%s DESTINATION=%s SILENT=%s\n", - ses->channel, destkey, checkIsSilent(ses)) < 0) { - strcpyerr(ses, "IO_ERROR"); - goto error; - } - if ((rep = sam3ReadReply(conn->fd)) == NULL) { - strcpyerr(ses, "IO_ERROR"); - goto error; - } - if (!ses->silent) { - if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { - const char *v = sam3FindField(rep, "RESULT"); - // - strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR")); - sam3CloseConnectionInternal(conn); - free(conn); - conn = NULL; - } else { - // no error - strcpyerr(ses, NULL); - } - } - sam3FreeFieldList(rep); - if (conn != NULL) { - strcpy(conn->destkey, destkey); - conn->ses = ses; - conn->next = ses->connlist; - ses->connlist = conn; - } - return conn; - error: - sam3CloseConnectionInternal(conn); - free(conn); - return NULL; - } - return NULL; -} - -Sam3Connection *sam3StreamAccept(Sam3Session *ses) { - if (ses != NULL) { - SAMFieldList *rep = NULL; - char repstr[1024]; - Sam3Connection *conn; - // - if (ses->type != SAM3_SESSION_STREAM) { - strcpyerr(ses, "INVALID_SESSION_TYPE"); - return NULL; - } - if (ses->fd < 0) { - strcpyerr(ses, "INVALID_SESSION"); - return NULL; - } - if ((conn = calloc(1, sizeof(Sam3Connection))) == NULL) { - strcpyerr(ses, "NO_MEMORY"); - return NULL; - } - if ((conn->fd = sam3HandshakeIP(ses->ip, ses->port)) < 0) { - strcpyerr(ses, "IO_ERROR_SK"); - goto error; - } - if (sam3tcpPrintf(conn->fd, "STREAM ACCEPT ID=%s\n", ses->channel) < 0) { - strcpyerr(ses, "IO_ERROR_PF"); - goto error; - } - if ((rep = sam3ReadReply(conn->fd)) == NULL) { - strcpyerr(ses, "IO_ERROR_RP"); - goto error; - } - if (!ses->silent) { - if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { - const char *v = sam3FindField(rep, "RESULT"); - // - strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES")); - goto error; - } - } - if (sam3tcpReceiveStr(conn->fd, repstr, sizeof(repstr)) < 0) { - strcpyerr(ses, "IO_ERROR_RP1"); - goto error; - } - sam3FreeFieldList(rep); - if ((rep = sam3ParseReply(repstr)) != NULL) { - const char *v = sam3FindField(rep, "RESULT"); - // - strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES1")); - goto error; - } - if (!sam3CheckValidKeyLength(repstr)) { - strcpyerr(ses, "INVALID_KEY"); - goto error; - } - sam3FreeFieldList(rep); - strcpy(conn->destkey, repstr); - conn->ses = ses; - conn->next = ses->connlist; - ses->connlist = conn; - strcpyerr(ses, NULL); - return conn; - error: - if (rep != NULL) - sam3FreeFieldList(rep); - sam3CloseConnectionInternal(conn); - free(conn); - return NULL; - } - return NULL; -} - -const char *checkIsSilent(Sam3Session *ses) { - if (ses->silent == true) { - return "true"; - } else { - return "false"; - } -} - -int sam3StreamForward(Sam3Session *ses, const char *hostname, int port) { - if (ses != NULL) { - SAMFieldList *rep = NULL; - // - if (ses->type != SAM3_SESSION_STREAM) { - strcpyerr(ses, "INVALID_SESSION_TYPE"); - return -1; - } - if (ses->fd < 0) { - strcpyerr(ses, "INVALID_SESSION"); - return -1; - } - if (ses->fwd_fd >= 0) { - strcpyerr(ses, "DUPLICATE_FORWARD"); - return -1; - } - if ((ses->fwd_fd = sam3HandshakeIP(ses->ip, ses->port)) < 0) { - strcpyerr(ses, "IO_ERROR_SK"); - goto error; - } - if (sam3tcpPrintf(ses->fwd_fd, - "STREAM FORWARD ID=%s PORT=%d HOST=%s SILENT=%s\n", - ses->channel, port, hostname, checkIsSilent(ses)) < 0) { - strcpyerr(ses, "IO_ERROR_PF"); - goto error; - } - if ((rep = sam3ReadReply(ses->fwd_fd)) == NULL) { - strcpyerr(ses, "IO_ERROR_RP"); - goto error; - } - if (!sam3IsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { - const char *v = sam3FindField(rep, "RESULT"); - // - strcpyerr(ses, (v != NULL && v[0] ? v : "I2P_ERROR_RES")); - goto error; - } - sam3FreeFieldList(rep); - strcpyerr(ses, NULL); - return 0; - error: - if (rep != NULL) - sam3FreeFieldList(rep); - return -1; - } - return -1; -} - -int sam3DatagramSend(Sam3Session *ses, const char *destkey, const void *buf, - size_t bufsize) { - if (ses != NULL) { - char *dbuf; - int res, dbufsz; - // - if (ses->type == SAM3_SESSION_STREAM) { - strcpyerr(ses, "INVALID_SESSION_TYPE"); - return -1; - } - if (ses->fd < 0) { - strcpyerr(ses, "INVALID_SESSION"); - return -1; - } - if (destkey == NULL || !sam3CheckValidKeyLength(destkey)) { - strcpyerr(ses, "INVALID_KEY"); - return -1; - } - if (buf == NULL || bufsize < 1 || bufsize > 31744) { - strcpyerr(ses, "INVALID_DATA"); - return -1; - } - dbufsz = bufsize + 4 + SAM3_PUBKEY_SIZE + 1 + strlen(ses->channel) + 1; - if ((dbuf = malloc(dbufsz)) == NULL) { - strcpyerr(ses, "OUT_OF_MEMORY"); - return -1; - } - sprintf(dbuf, "3.0 %s %s\n", ses->channel, destkey); - memcpy(dbuf + strlen(dbuf), buf, bufsize); - res = sam3udpSendToIP(ses->ip, ses->port, dbuf, dbufsz); - free(dbuf); - strcpyerr(ses, (res < 0 ? "IO_ERROR" : NULL)); - return (res < 0 ? -1 : 0); - } - return -1; -} - -ssize_t sam3DatagramReceive(Sam3Session *ses, void *buf, size_t bufsize) { - if (ses != NULL) { - SAMFieldList *rep; - const char *v; - ssize_t size = 0; - // - if (ses->type == SAM3_SESSION_STREAM) { - strcpyerr(ses, "INVALID_SESSION_TYPE"); - return -1; - } - if (ses->fd < 0) { - strcpyerr(ses, "INVALID_SESSION"); - return -1; - } - if (buf == NULL || bufsize < 1) { - strcpyerr(ses, "INVALID_BUFFER"); - return -1; - } - if ((rep = sam3ReadReply(ses->fd)) == NULL) { - strcpyerr(ses, "IO_ERROR"); - return -1; - } - if (!sam3IsGoodReply(rep, "DATAGRAM", "RECEIVED", "SIZE", NULL)) { - strcpyerr(ses, "I2P_ERROR"); - sam3FreeFieldList(rep); - return -1; - } - // - if ((v = sam3FindField(rep, "DESTINATION")) != NULL && - sam3CheckValidKeyLength(v)) - strncpy(ses->destkey, v, sizeof(ses->destkey)); - v = sam3FindField(rep, "SIZE"); // we have this field -- for sure - if (!v[0] || !isdigit(*v)) { - strcpyerr(ses, "I2P_ERROR_SIZE"); - sam3FreeFieldList(rep); - return -1; - } - // - while (*v && isdigit(*v)) { - if ((size = size * 10 + v[0] - '0') > bufsize) { - strcpyerr(ses, "I2P_ERROR_BUFFER_TOO_SMALL"); - sam3FreeFieldList(rep); - return -1; - } - ++v; - } - // - if (*v) { - strcpyerr(ses, "I2P_ERROR_SIZE"); - sam3FreeFieldList(rep); - return -1; - } - sam3FreeFieldList(rep); - // - if (sam3tcpReceive(ses->fd, buf, size) != size) { - strcpyerr(ses, "IO_ERROR"); - return -1; - } - strcpyerr(ses, NULL); - return size; - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -// output 8 bytes for every 5 input -// return size or <0 on error -ssize_t sam3Base32Encode(char *dest, size_t destsz, const void *srcbuf, - size_t srcsize) { - if (dest != NULL && srcbuf != NULL && srcsize >= 0) { - static const char *const b32chars = "abcdefghijklmnopqrstuvwxyz234567="; - const unsigned char *src = (const unsigned char *)srcbuf; - ssize_t destsize = 0; - // - while (srcsize > 0) { - int blksize = (srcsize < 5 ? srcsize : 5); - unsigned char n[8]; - // - memset(n, 0, sizeof(n)); - switch (blksize) { - case 5: - n[7] = (src[4] & 0x1f); - n[6] = ((src[4] & 0xe0) >> 5); - case 4: - n[6] |= ((src[3] & 0x03) << 3); - n[5] = ((src[3] & 0x7c) >> 2); - n[4] = ((src[3] & 0x80) >> 7); - case 3: - n[4] |= ((src[2] & 0x0f) << 1); - n[3] = ((src[2] & 0xf0) >> 4); - case 2: - n[3] |= ((src[1] & 0x01) << 4); - n[2] = ((src[1] & 0x3e) >> 1); - n[1] = ((src[1] & 0xc0) >> 6); - case 1: - n[1] |= ((src[0] & 0x07) << 2); - n[0] = ((src[0] & 0xf8) >> 3); - break; - } - src += blksize; - srcsize -= blksize; - // pad - switch (blksize) { - case 1: - n[2] = n[3] = 32; - case 2: - n[4] = 32; - case 3: - n[5] = n[6] = 32; - case 4: - n[7] = 32; - case 5: - break; - } - // output - if (destsize + 8 <= destsz) { - for (int f = 0; f < 8; ++f) - *dest++ = b32chars[n[f]]; - } - destsize += 8; - } - if (destsize <= destsz) { - *dest++ = 0; // make valid asciiz string - } - return destsize; - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -// output 8 bytes for every 5 input -// return size or <0 on error -ssize_t sam3Base32Decode(char *dest, size_t destsz, const void *srcbuf, - size_t srcsize) { - if (dest != NULL && srcbuf != NULL && srcsize >= 0) { - static const char *const b32chars = "abcdefghijklmnopqrstuvwxyz234567="; - const unsigned char *src = (const unsigned char *)srcbuf; - int destsize = 0; - // - while (srcsize > 0) { - int blksize = (srcsize < 5 ? srcsize : 5); - unsigned char n[8]; - // - memset(n, 0, sizeof(n)); - switch (blksize) { - case 5: - n[7] = (src[4] & 0x1f); - n[6] = ((src[4] & 0xe0) >> 5); - case 4: - n[6] |= ((src[3] & 0x03) << 3); - n[5] = ((src[3] & 0x7c) >> 2); - n[4] = ((src[3] & 0x80) >> 7); - case 3: - n[4] |= ((src[2] & 0x0f) << 1); - n[3] = ((src[2] & 0xf0) >> 4); - case 2: - n[3] |= ((src[1] & 0x01) << 4); - n[2] = ((src[1] & 0x3e) >> 1); - n[1] = ((src[1] & 0xc0) >> 6); - case 1: - n[1] |= ((src[0] & 0x07) << 2); - n[0] = ((src[0] & 0xf8) >> 3); - break; - } - src += blksize; - srcsize -= blksize; - // pad - switch (blksize) { - case 1: - n[2] = n[3] = 32; - case 2: - n[4] = 32; - case 3: - n[5] = n[6] = 32; - case 4: - n[7] = 32; - case 5: - break; - } - // output - if (destsize + 8 <= destsz) { - for (int f = 0; f < 8; ++f) - *dest++ = b32chars[n[f]]; - } - destsize += 8; - } - if (destsize <= destsz) { - *dest++ = 0; // make valid asciiz string - } - return destsize; - } - return -1; -} diff --git a/supportlibs/libsam3/src/libsam3/libsam3.h b/supportlibs/libsam3/src/libsam3/libsam3.h deleted file mode 100644 index 16942f594..000000000 --- a/supportlibs/libsam3/src/libsam3/libsam3.h +++ /dev/null @@ -1,314 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#ifndef LIBSAM3_H -#define LIBSAM3_H - -#include -#include -#include - -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -#undef ssize_t -#ifdef _WIN64 -typedef signed int64 ssize_t; -typedef int ssize_t; -#endif /* _WIN64 */ -#endif /* _SSIZE_T_DEFINED */ - -#ifdef __cplusplus -extern "C" { -#endif - -//////////////////////////////////////////////////////////////////////////////// -extern int libsam3_debug; - -//////////////////////////////////////////////////////////////////////////////// -#define SAM3_HOST_DEFAULT (NULL) -#define SAM3_PORT_DEFAULT (0) - -#define SAM3_DESTINATION_TRANSIENT (NULL) - -#define SAM3_PUBKEY_SIZE (516) -#define SAM3_CERT_SIZE (100) -#define SAM3_PRIVKEY_MIN_SIZE (884) - -//////////////////////////////////////////////////////////////////////////////// -/* returns fd or -1 */ -/* 'ip': host IP; can be NULL */ -extern int sam3tcpConnect(const char *hostname, int port, uint32_t *ip); -extern int sam3tcpConnectIP(uint32_t ip, int port); - -/* <0: error; 0: ok */ -extern int sam3tcpDisconnect(int fd); - -/* <0: error; 0: ok */ -extern int sam3tcpSetTimeoutSend(int fd, int timeoutms); - -/* <0: error; 0: ok */ -extern int sam3tcpSetTimeoutReceive(int fd, int timeoutms); - -/* <0: error; 0: ok */ -/* sends the whole buffer */ -extern int sam3tcpSend(int fd, const void *buf, size_t bufSize); - -/* <0: received (-res) bytes; read error */ -/* can return less that requesten bytes even if `allowPartial` is 0 when - * connection is closed */ -extern ssize_t sam3tcpReceiveEx(int fd, void *buf, size_t bufSize, - int allowPartial); - -extern ssize_t sam3tcpReceive(int fd, void *buf, size_t bufSize); - -extern int sam3tcpPrintf(int fd, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); - -extern int sam3tcpReceiveStr(int fd, char *dest, size_t maxSize); - -/* pass NULL for 'localhost' and 0 for 7655 */ -/* 'ip': host IP; can be NULL */ -extern int sam3udpSendTo(const char *hostname, int port, const void *buf, - size_t bufSize, uint32_t *ip); -extern int sam3udpSendToIP(uint32_t ip, int port, const void *buf, - size_t bufSize); - -//////////////////////////////////////////////////////////////////////////////// -typedef struct SAMFieldList { - char *name; - char *value; - struct SAMFieldList *next; -} SAMFieldList; - -extern void sam3FreeFieldList(SAMFieldList *list); -extern void sam3DumpFieldList(const SAMFieldList *list); - -/* read and parse SAM reply */ -/* NULL: error; else: list of fields */ -/* first item is always 2-word reply, with first word in name and second in - * value */ -extern SAMFieldList *sam3ReadReply(int fd); - -extern SAMFieldList *sam3ParseReply(const char *rep); - -/* - * example: - * r0: 'HELLO' - * r1: 'REPLY' - * field: NULL or 'RESULT' - * VALUE: NULL or 'OK' - * returns bool - */ -extern int sam3IsGoodReply(const SAMFieldList *list, const char *r0, - const char *r1, const char *field, - const char *value); - -extern const char *sam3FindField(const SAMFieldList *list, const char *field); - -//////////////////////////////////////////////////////////////////////////////// -/* pass NULL for 'localhost' and 0 for 7656 */ -/* returns <0 on error or socket fd on success */ -extern int sam3Handshake(const char *hostname, int port, uint32_t *ip); -extern int sam3HandshakeIP(uint32_t ip, int port); - -//////////////////////////////////////////////////////////////////////////////// -typedef enum { - SAM3_SESSION_RAW, - SAM3_SESSION_DGRAM, - SAM3_SESSION_STREAM -} Sam3SessionType; - -typedef enum { - DSA_SHA1, - ECDSA_SHA256_P256, - ECDSA_SHA384_P384, - ECDSA_SHA512_P521, - EdDSA_SHA512_Ed25519 -} Sam3SigType; - -typedef struct Sam3Session { - Sam3SessionType type; - Sam3SigType sigType; - int fd; - char privkey[SAM3_PRIVKEY_MIN_SIZE + 1]; // destination private key (asciiz) - char pubkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + - 1]; // destination public key (asciiz) - char channel[66]; // name of this sam session (asciiz) - char destkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + - 1]; // for DGRAM sessions (asciiz) - // int destsig; - char error[32]; // error message (asciiz) - uint32_t ip; - int port; // this will be changed to UDP port for DRAM/RAW (can be 0) - struct Sam3Connection *connlist; // list of opened connections - int fwd_fd; - bool silent; -} Sam3Session; - -typedef struct Sam3Connection { - Sam3Session *ses; - struct Sam3Connection *next; - int fd; - char destkey[SAM3_PUBKEY_SIZE + SAM3_CERT_SIZE + - 1]; // remote destination public key (asciiz) - int destcert; - char error[32]; // error message (asciiz) -} Sam3Connection; - -//////////////////////////////////////////////////////////////////////////////// -/* - * create SAM session - * pass NULL as hostname for 'localhost' and 0 as port for 7656 - * pass NULL as privkey to create TRANSIENT session - * 'params' can be NULL - * see http://www.i2p2.i2p/i2cp.html#options for common options, - * and http://www.i2p2.i2p/streaming.html#options for STREAM options - * if result<0: error, 'ses' fields are undefined, no need to call - * sam3CloseSession() if result==0: ok, all 'ses' fields are filled - * TODO: don't clear 'error' field on error (and set it to something meaningful) - */ -extern int sam3CreateSession(Sam3Session *ses, const char *hostname, int port, - const char *privkey, Sam3SessionType type, - Sam3SigType sigType, const char *params); - -/* - * create SAM session with SILENT=True - * pass NULL as hostname for 'localhost' and 0 as port for 7656 - * pass NULL as privkey to create TRANSIENT session - * 'params' can be NULL - * see http://www.i2p2.i2p/i2cp.html#options for common options, - * and http://www.i2p2.i2p/streaming.html#options for STREAM options - * if result<0: error, 'ses' fields are undefined, no need to call - * sam3CloseSession() if result==0: ok, all 'ses' fields are filled - * TODO: don't clear 'error' field on error (and set it to something meaningful) - */ -extern int sam3CreateSilentSession(Sam3Session *ses, const char *hostname, - int port, const char *privkey, - Sam3SessionType type, Sam3SigType sigType, - const char *params); - -/* - * close SAM session (and all it's connections) - * returns <0 on error, 0 on ok - * 'ses' must be properly initialized - */ -extern int sam3CloseSession(Sam3Session *ses); - -/* - * check to see if a SAM session is silent and output - * characters for use with sam3tcpPrintf() checkIsSilent - */ - -const char *checkIsSilent(Sam3Session *ses); - -/* - * Check to make sure that the destination in use is of a valid length, returns - * 1 if true and 0 if false. - */ -int sam3CheckValidKeyLength(const char *pubkey); - -/* - * open stream connection to 'destkey' endpoint - * 'destkey' is 516-byte public key (asciiz) - * returns <0 on error, fd on ok - * you still have to call sam3CloseSession() on failure - * sets ses->error on error - */ -extern Sam3Connection *sam3StreamConnect(Sam3Session *ses, const char *destkey); - -/* - * accepts stream connection and sets 'destkey' - * 'destkey' is 516-byte public key - * returns <0 on error, fd on ok - * you still have to call sam3CloseSession() on failure - * sets ses->error on error - * note that there is no timeouts for now, but you can use sam3tcpSetTimeout*() - */ -extern Sam3Connection *sam3StreamAccept(Sam3Session *ses); - -/* - * sets up forwarding stream connection - * returns <0 on error, 0 on ok - * you still have to call sam3CloseSession() on failure - * sets ses->error on error - * note that there is no timeouts for now, but you can use sam3tcpSetTimeout*() - */ -extern int sam3StreamForward(Sam3Session *ses, const char *hostname, int port); - -/* - * close SAM connection - * returns <0 on error, 0 on ok - * 'conn' must be properly initialized - * 'conn' is invalid after call - */ -extern int sam3CloseConnection(Sam3Connection *conn); - -//////////////////////////////////////////////////////////////////////////////// -/* - * generate new keypair - * fills 'privkey' and 'pubkey' only - * you should not call sam3CloseSession() on 'ses' - * will not set 'error' field - * returns <0 on error, 0 on ok - */ -extern int sam3GenerateKeys(Sam3Session *ses, const char *hostname, int port, - int sigType); - -/* - * do name lookup (something like gethostbyname()) - * fills 'destkey' only - * you should not call sam3CloseSession() on 'ses' - * will set 'error' field - * returns <0 on error, 0 on ok - */ -extern int sam3NameLookup(Sam3Session *ses, const char *hostname, int port, - const char *name); - -//////////////////////////////////////////////////////////////////////////////// -/* - * sends datagram to 'destkey' endpoint - * 'destkey' is 516-byte public key - * returns <0 on error, 0 on ok - * you still have to call sam3CloseSession() on failure - * sets ses->error on error - * don't send datagrams bigger than 31KB! - */ -extern int sam3DatagramSend(Sam3Session *ses, const char *destkey, - const void *buf, size_t bufsize); - -/* - * receives datagram and sets 'destkey' to source pubkey (if not RAW) - * returns <0 on error (buffer too small is error too) or number of bytes - * written to 'buf' you still have to call sam3CloseSession() on failure sets - * ses->error on error will necer receive datagrams bigger than 31KB (32KB for - * RAW) - */ -extern ssize_t sam3DatagramReceive(Sam3Session *ses, void *buf, size_t bufsize); - -/* - * generate random sam channel name - * return the size of the string - */ -extern size_t sam3GenChannelName(char *dest, size_t minlen, size_t maxlen); - -//////////////////////////////////////////////////////////////////////////////// -// NOT including '\0' terminator -static inline size_t sam3Base32EncodedLength(size_t size) { - return (((size + 5 - 1) / 5) * 8); -} - -// output 8 bytes for every 5 input -// return size or <0 on error -extern ssize_t sam3Base32Encode(char *dest, size_t destsz, const void *srcbuf, - size_t srcsize); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/supportlibs/libsam3/src/libsam3a/libsam3a.c b/supportlibs/libsam3/src/libsam3a/libsam3a.c deleted file mode 100644 index e5983b008..000000000 --- a/supportlibs/libsam3/src/libsam3a/libsam3a.c +++ /dev/null @@ -1,1771 +0,0 @@ -/* async SAMv3 library - * - * This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include "libsam3a.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __MINGW32__ -//#include -#include -#include -#include -#ifndef MSG_NOSIGNAL -#define MSG_NOSIGNAL 0 -#endif -#ifndef SHUT_RDWR -#define SHUT_RDWR 2 -#endif -#endif - -#ifdef __unix__ -#include -#include -#include -#include -#include -#include -#include -#endif -//////////////////////////////////////////////////////////////////////////////// -int libsam3a_debug = 0; - -#define DEFAULT_TCP_PORT (7656) -#define DEFAULT_UDP_PORT (7655) - -//////////////////////////////////////////////////////////////////////////////// -uint64_t sam3atimeval2ms(const struct timeval *tv) { - return ((uint64_t)tv->tv_sec) * 1000 + ((uint64_t)tv->tv_usec) / 1000; -} - -void sam3ams2timeval(struct timeval *tv, uint64_t ms) { - tv->tv_sec = ms / 1000; - tv->tv_usec = (ms % 1000) * 1000; -} - -//////////////////////////////////////////////////////////////////////////////// -static inline int isValidKeyChar(char ch) { - return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || - (ch >= '0' && ch <= '9') || ch == '-' || ch == '~'; -} - -int sam3aIsValidPubKey(const char *key) { - if (key != NULL && strlen(key) == SAM3A_PUBKEY_SIZE) { - for (int f = 0; f < SAM3A_PUBKEY_SIZE; ++f) - if (!isValidKeyChar(key[f])) - return 0; - return 1; - } - return 0; -} - -int sam3aIsValidPrivKey(const char *key) { - if (key != NULL && strlen(key) == SAM3A_PRIVKEY_SIZE) { - for (int f = 0; f < SAM3A_PRIVKEY_SIZE; ++f) - if (!isValidKeyChar(key[f])) - return 0; - return 1; - } - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -/* -static int sam3aSocketSetTimeoutSend (int fd, int timeoutms) { - if (fd >= 0 && timeoutms >= 0) { - struct timeval tv; - // - ms2timeval(&tv, timeoutms); - return (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 ? -1 : -0); - } - return -1; -} - - -static int sam3aSocketSetTimeoutReceive (int fd, int timeoutms) { - if (fd >= 0 && timeoutms >= 0) { - struct timeval tv; - // - ms2timeval(&tv, timeoutms); - return (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ? -1 : -0); - } - return -1; -} -*/ - -static int sam3aBytesAvail(int fd) { - int av = 0; - // - if (ioctl(fd, FIONREAD, &av) < 0) - return -1; - return av; -} - -static uint32_t sam3aResolveHost(const char *hostname) { - struct hostent *host; - // - if (hostname == NULL || !hostname[0]) - return 0; - if ((host = gethostbyname(hostname)) == NULL || host->h_name == NULL || - !host->h_addr_list[0][0]) { - if (libsam3a_debug) - fprintf(stderr, "ERROR: can't resolve '%s'\n", hostname); - return 0; - } - return ((struct in_addr *)host->h_addr_list[0])->s_addr; -} - -static int sam3aConnect(uint32_t ip, int port, int *complete) { - int fd, val = 1; - // - if (complete != NULL) - *complete = 0; - if (ip == 0 || ip == 0xffffffffUL || port < 1 || port > 65535) - return -1; - // - // yes, this is Linux-specific; you know what? i don't care. - if ((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) - return -1; - // - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); - // - for (;;) { - struct sockaddr_in addr; - // - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = ip; - // - if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { - if (errno == EINPROGRESS) - break; // the process is started - if (errno != EINTR) { - close(fd); - return -1; - } - } else { - // connection complete - if (complete != NULL) - *complete = 1; - break; - } - } - // - return fd; -} - -// <0: error; 0: ok -static int sam3aDisconnect(int fd) { - if (fd >= 0) { - shutdown(fd, SHUT_RDWR); - return close(fd); - } - // - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -// <0: error; >=0: bytes sent -static int sam3aSendBytes(int fd, const void *buf, int bufSize) { - const char *c = (const char *)buf; - int total = 0; - // - if (fd < 0 || (buf == NULL && bufSize > 0)) - return -1; - // - while (bufSize > 0) { - int wr = send(fd, c, bufSize, MSG_NOSIGNAL); - // - if (wr < 0) { - if (errno == EINTR) - continue; // interrupted by signal - if (errno == EAGAIN || errno == EWOULDBLOCK) { - // bufSize is too big - if (bufSize == 1) - break; // can't send anything - // try to send a half of a buffer - if ((wr = sam3aSendBytes(fd, c, bufSize / 2)) < 0) - return wr; // error - } else { - return -1; // alas - } - } - // - if (wr == 0) - break; // can't send anything - c += wr; - bufSize -= wr; - total += wr; - } - // - return total; -} - -/* <0: error; >=0: bytes received */ -/* note that you should call this function when there is some bytes to read, so - * 0 means 'connection closed' */ -/* -static int sam3aReceive (int fd, void *buf, int bufSize) { - char *c = (char *)buf; - int total = 0; - // - if (fd < 0 || (buf == NULL && bufSize > 0)) return -1; - // - while (bufSize > 0) { - int av = sam3aBytesAvail(fd), rd; - // - if (av == 0) break; // no more - if (av > bufSize) av = bufSize; - rd = recv(fd, c, av, 0); - if (rd < 0) { - if (errno == EINTR) continue; // interrupted by signal - if (errno == EAGAIN || errno == EWOULDBLOCK) break; // the thing that -should not be return -1; // error - } - if (rd == 0) break; - c += rd; - bufSize -= rd; - total += rd; - } - // - return total; -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -char *sam3PrintfVA(int *plen, const char *fmt, va_list app) { - char buf[1024], *p = buf; - int size = sizeof(buf) - 1, len = 0; - // - if (plen != NULL) - *plen = 0; - for (;;) { - va_list ap; - char *np; - int n; - // - va_copy(ap, app); - n = vsnprintf(p, size, fmt, ap); - va_end(ap); - // - if (n > -1 && n < size) { - len = n; - break; - } - if (n > -1) - size = n + 1; - else - size *= 2; - if (p == buf) { - if ((p = malloc(size)) == NULL) - return NULL; - } else { - if ((np = realloc(p, size)) == NULL) { - free(p); - return NULL; - } - p = np; - } - } - // - if (p == buf) { - if ((p = malloc(len + 1)) == NULL) - return NULL; - memcpy(p, buf, len + 1); - } - if (plen != NULL) - *plen = len; - return p; -} - -__attribute__((format(printf, 2, 3))) char *sam3Printf(int *plen, - const char *fmt, ...) { - va_list ap; - char *res; - // - va_start(ap, fmt); - res = sam3PrintfVA(plen, fmt, ap); - va_end(ap); - // - return res; -} - -/* - * check if we have EOL in received socket data - * this function should be called when sam3aBytesAvail() result > 0 - * return: <0: error; 0: no EOL in bytes2check, else: # of bytes to EOL - * (including EOL itself) - */ -/* -static int sam3aCheckEOL (int fd, int bytes2check) { - char *d = dest; - // - if (bytes2check < 0 || fd < 0) return -1; - memset(dest, 0, maxSize); - while (maxSize > 1) { - char *e; - int rd = recv(fd, d, maxSize-1, MSG_PEEK); - // - if (rd < 0 && errno == EINTR) continue; // interrupted by signal - if (rd == 0) { - rd = recv(fd, d, 1, 0); - if (rd < 0 && errno == EINTR) continue; // interrupted by signal - if (d[0] == '\n') { - d[0] = 0; // remove '\n' - return 0; - } - } else { - if (rd < 0) return -1; // error or connection closed; alas - } - // check for EOL - d[maxSize-1] = 0; - if ((e = strchr(d, '\n')) != NULL) { - rd = e-d+1; // bytes to receive - if (sam3atcpReceive(fd, d, rd) < 0) return -1; // alas - d[rd-1] = 0; // remove '\n' - return 0; // done - } else { - // let's receive this part and go on - if (sam3atcpReceive(fd, d, rd) < 0) return -1; // alas - maxSize -= rd; - d += rd; - } - } - // alas, the string is too big - return -1; -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -/* -int sam3audpSendToIP (uint32_t ip, int port, const void *buf, int bufSize) { - struct sockaddr_in addr; - int fd, res; - // - if (buf == NULL || bufSize < 1) return -1; - if (port < 1 || port > 65535) port = 7655; - // - if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - if (libsam3a_debug) fprintf(stderr, "ERROR: can't create socket\n"); - return -1; - } - // - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = ip; - // - res = sendto(fd, buf, bufSize, 0, (struct sockaddr *)&addr, sizeof(addr)); - // - if (res < 0) { - if (libsam3a_debug) { - res = errno; - fprintf(stderr, "UDP ERROR (%d): %s\n", res, strerror(res)); - } - res = -1; - } else { - if (libsam3a_debug) fprintf(stderr, "UDP: %d bytes sent\n", res); - } - // - close(fd); - // - return (res >= 0 ? 0 : -1); -} - - -int sam3audpSendTo (const char *hostname, int port, const void *buf, int -bufSize, uint32_t *ip) { struct hostent *host = NULL; - // - if (buf == NULL || bufSize < 1) return -1; - if (hostname == NULL || !hostname[0]) hostname = "localhost"; - if (port < 1 || port > 65535) port = 7655; - // - host = gethostbyname(hostname); - if (host == NULL || host->h_name == NULL || !host->h_name[0]) { - if (libsam3a_debug) fprintf(stderr, "ERROR: can't resolve '%s'\n", -hostname); return -1; - } - // - if (ip != NULL) *ip = ((struct in_addr *)host->h_addr)->s_addr; - return sam3audpSendToIP(((struct in_addr *)host->h_addr)->s_addr, port, buf, -bufSize); -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -typedef struct SAMFieldList { - char *name; - char *value; - struct SAMFieldList *next; -} SAMFieldList; - -static void sam3aFreeFieldList(SAMFieldList *list) { - while (list != NULL) { - SAMFieldList *c = list; - // - list = list->next; - if (c->name != NULL) - free(c->name); - if (c->value != NULL) - free(c->value); - free(c); - } -} - -static void sam3aDumpFieldList(const SAMFieldList *list) { - for (; list != NULL; list = list->next) { - fprintf(stderr, "%s=[%s]\n", list->name, list->value); - } -} - -static const char *sam3aFindField(const SAMFieldList *list, const char *field) { - if (list != NULL && field != NULL) { - for (list = list->next; list != NULL; list = list->next) { - if (list->name != NULL && strcmp(field, list->name) == 0) - return list->value; - } - } - return NULL; -} - -static char *xstrdup(const char *s, int len) { - if (len >= 0) { - char *res = malloc(len + 1); - // - if (res != NULL) { - if (len > 0) - memcpy(res, s, len); - res[len] = 0; - } - // - return res; - } - // - return NULL; -} - -// returns NULL if there are no more tokens -static inline const char *xstrtokend(const char *s) { - while (*s && isspace(*s)) - ++s; - // - if (*s) { - char qch = 0; - // - while (*s) { - if (*s == qch) { - qch = 0; - } else if (*s == '"') { - qch = *s; - } else if (qch) { - if (*s == '\\' && s[1]) - ++s; - } else if (isspace(*s)) { - break; - } - ++s; - } - } else { - s = NULL; - } - // - return s; -} - -SAMFieldList *sam3aParseReply(const char *rep) { - SAMFieldList *first = NULL, *last, *c; - const char *p = rep, *e, *e1; - // - // first 2 words - while (*p && isspace(*p)) - ++p; - if ((e = xstrtokend(p)) == NULL) - return NULL; - if ((e1 = xstrtokend(e)) == NULL) - return NULL; - // - if ((first = last = c = malloc(sizeof(SAMFieldList))) == NULL) - return NULL; - c->next = NULL; - c->name = c->value = NULL; - if ((c->name = xstrdup(p, e - p)) == NULL) - goto error; - while (*e && isspace(*e)) - ++e; - if ((c->value = xstrdup(e, e1 - e)) == NULL) - goto error; - // - p = e1; - while (*p) { - while (*p && isspace(*p)) - ++p; - if ((e = xstrtokend(p)) == NULL) - break; // no more tokens - // - if (libsam3a_debug) - fprintf(stderr, "<%s>\n", p); - // - if ((c = malloc(sizeof(SAMFieldList))) == NULL) - return NULL; - c->next = NULL; - c->name = c->value = NULL; - last->next = c; - last = c; - // - if ((e1 = memchr(p, '=', e - p)) != NULL) { - // key=value - if ((c->name = xstrdup(p, e1 - p)) == NULL) - goto error; - if ((c->value = xstrdup(e1 + 1, e - e1 - 1)) == NULL) - goto error; - } else { - // only key (there is no such replies in SAMv3, but... - if ((c->name = xstrdup(p, e - p)) == NULL) - goto error; - if ((c->value = strdup("")) == NULL) - goto error; - } - p = e; - } - // - if (libsam3a_debug) - sam3aDumpFieldList(first); - // - return first; -error: - sam3aFreeFieldList(first); - return NULL; -} - -// example: -// r0: 'HELLO' -// r1: 'REPLY' -// field: NULL or 'RESULT' -// VALUE: NULL or 'OK' -// returns bool -int sam3aIsGoodReply(const SAMFieldList *list, const char *r0, const char *r1, - const char *field, const char *value) { - if (list != NULL && list->name != NULL && list->value != NULL) { - if (r0 != NULL && strcmp(r0, list->name) != 0) - return 0; - if (r1 != NULL && strcmp(r1, list->value) != 0) - return 0; - if (field != NULL) { - for (list = list->next; list != NULL; list = list->next) { - if (list->name == NULL || list->value == NULL) - return 0; // invalid list, heh - if (strcmp(field, list->name) == 0) { - if (value != NULL && strcmp(value, list->value) != 0) - return 0; - return 1; - } - } - } - return 1; - } - return 0; -} - -// NULL: error; else: list of fields -// first item is always 2-word reply, with first word in name and second in -// value -/* -SAMFieldList *sam3aReadReply (int fd) { - char rep[2048]; // should be enough for any reply - // - if (sam3atcpReceiveStr(fd, rep, sizeof(rep)) < 0) return NULL; - if (libsam3a_debug) fprintf(stderr, "SAM REPLY: [%s]\n", rep); - return sam3aParseReply(rep); -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -// by Bob Jenkins -// public domain -// http://burtleburtle.net/bob/rand/smallprng.html -// -//////////////////////////////////////////////////////////////////////////////// -typedef struct { - uint32_t a, b, c, d; -} BJRandCtx; - -#define BJPRNG_ROT(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) - -static uint32_t bjprngRand(BJRandCtx *x) { - uint32_t e; - /* original: - e = x->a-BJPRNG_ROT(x->b, 27); - x->a = x->b^BJPRNG_ROT(x->c, 17); - x->b = x->c+x->d; - x->c = x->d+e; - x->d = e+x->a; - */ - /* better, but slower at least in idiotic m$vc */ - e = x->a - BJPRNG_ROT(x->b, 23); - x->a = x->b ^ BJPRNG_ROT(x->c, 16); - x->b = x->c + BJPRNG_ROT(x->d, 11); - x->c = x->d + e; - x->d = e + x->a; - // - return x->d; -} - -static void bjprngInit(BJRandCtx *x, uint32_t seed) { - x->a = 0xf1ea5eed; - x->b = x->c = x->d = seed; - for (int i = 0; i < 20; ++i) - bjprngRand(x); -} - -static inline uint32_t hashint(uint32_t a) { - a -= (a << 6); - a ^= (a >> 17); - a -= (a << 9); - a ^= (a << 4); - a -= (a << 3); - a ^= (a << 10); - a ^= (a >> 15); - return a; -} - -static uint32_t genSeed(void) { - volatile uint32_t seed = 1; - uint32_t res; -#ifndef WIN32 - struct sysinfo sy; - pid_t pid = getpid(); - // - sysinfo(&sy); - res = hashint((uint32_t)pid) ^ hashint((uint32_t)time(NULL)) ^ - hashint((uint32_t)sy.sharedram) ^ hashint((uint32_t)sy.bufferram) ^ - hashint((uint32_t)sy.uptime); -#else - res = hashint((uint32_t)GetCurrentProcessId()) ^ - hashint((uint32_t)GetTickCount()); -#endif - res += __sync_fetch_and_add(&seed, 1); - // - return hashint(res); -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aGenChannelName(char *dest, int minlen, int maxlen) { - BJRandCtx rc; - int len; - // - if (dest == NULL || minlen < 1 || maxlen < minlen || minlen > 65536 || - maxlen > 65536) - return -1; - bjprngInit(&rc, genSeed()); - len = minlen + (bjprngRand(&rc) % (maxlen - minlen + 1)); - while (len-- > 0) { - int ch = bjprngRand(&rc) % 64; - // - if (ch >= 0 && ch < 10) - ch += '0'; - else if (ch >= 10 && ch < 36) - ch += 'A' - 10; - else if (ch >= 36 && ch < 62) - ch += 'a' - 36; - else if (ch == 62) - ch = '-'; - else if (ch == 63) - ch = '_'; - else if (ch > 64) - abort(); - *dest++ = ch; - } - *dest++ = 0; - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aIsActiveSession(const Sam3ASession *ses) { - return (ses != NULL && ses->fd >= 0 && !ses->cancelled); -} - -int sam3aIsActiveConnection(const Sam3AConnection *conn) { - return (conn != NULL && conn->fd >= 0 && !conn->cancelled); -} - -//////////////////////////////////////////////////////////////////////////////// -static inline void strcpyerrs(Sam3ASession *ses, const char *errstr) { - // memset(ses->error, 0, sizeof(ses->error)); - ses->error[sizeof(ses->error) - 1] = 0; - if (errstr != NULL) - strncpy(ses->error, errstr, sizeof(ses->error) - 1); -} - -static inline void strcpyerrc(Sam3AConnection *conn, const char *errstr) { - // memset(conn->error, 0, sizeof(conn->error)); - conn->error[sizeof(conn->error) - 1] = 0; - if (errstr != NULL) - strncpy(conn->error, errstr, sizeof(conn->error) - 1); -} - -static void connDisconnect(Sam3AConnection *conn) { - conn->cbAIOProcessorR = conn->cbAIOProcessorW = NULL; - if (conn->aio.data != NULL) { - free(conn->aio.data); - conn->aio.data = NULL; - } - if (!conn->cancelled && conn->fd >= 0) { - conn->cancelled = 1; - shutdown(conn->fd, SHUT_RDWR); - if (conn->callDisconnectCB && conn->cb.cbDisconnected != NULL) - conn->cb.cbDisconnected(conn); - } -} - -static void sesDisconnect(Sam3ASession *ses) { - ses->cbAIOProcessorR = ses->cbAIOProcessorW = NULL; - if (ses->aio.data != NULL) { - free(ses->aio.data); - ses->aio.data = NULL; - } - if (!ses->cancelled && ses->fd >= 0) { - ses->cancelled = 1; - shutdown(ses->fd, SHUT_RDWR); - for (Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) - connDisconnect(c); - if (ses->callDisconnectCB && ses->cb.cbDisconnected != NULL) - ses->cb.cbDisconnected(ses); - } -} - -static void sesError(Sam3ASession *ses, const char *errstr) { - if (errstr == NULL || !errstr[0]) - errstr = "I2P_ERROR"; - strcpyerrs(ses, errstr); - if (ses->cb.cbError != NULL) - ses->cb.cbError(ses); - sesDisconnect(ses); -} - -static void connError(Sam3AConnection *conn, const char *errstr) { - if (errstr == NULL || !errstr[0]) - errstr = "I2P_ERROR"; - strcpyerrc(conn, errstr); - if (conn->cb.cbError != NULL) - conn->cb.cbError(conn); - connDisconnect(conn); -} - -//////////////////////////////////////////////////////////////////////////////// -static int aioSender(int fd, Sam3AIO *aio) { - int wr = sam3aSendBytes(fd, aio->data + aio->dataPos, - aio->dataUsed - aio->dataPos); - // - if (wr < 0) - return -1; - aio->dataPos += wr; - return 0; -} - -// dataUsed: max line size (with '\n') -// dataSize: must be at least (dataUsed+1) -static int aioLineReader(int fd, Sam3AIO *aio) { - // - for (;;) { - int av = sam3aBytesAvail(fd), rd; - // - if (av < 0) - return -1; - if (av == 0) - return 0; // do nothing - if (aio->dataPos >= aio->dataUsed - 1) - return -1; // line too long - if ((rd = (aio->dataUsed - 1) - aio->dataPos) > av) - rd = av; - if ((rd = recv(fd, aio->data + aio->dataPos, rd, MSG_PEEK)) < 0) { - if (errno == EINTR) - continue; - return -1; - } - if (rd == 0) - return 0; // do nothing - // now look for '\n' - for (int f = aio->dataPos; f < aio->dataPos + rd; ++f) { - if (aio->data[f] == '\n') { - // got it! - if (recv(fd, aio->data + aio->dataPos, f - aio->dataPos + 1, 0) != - f + 1) - return -1; // the thing that should not be - aio->data[f] = 0; // convert to asciiz - aio->dataUsed = aio->dataPos = f; // length - return 1; // '\n' found! - } - if (!aio->data[f]) - return -1; // there should not be zero bytes - } - // no '\n' found - if (recv(fd, aio->data + aio->dataPos, rd, 0) != rd) - return -1; // the thing that should not be - aio->dataPos += rd; - } -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioSesCmdReplyReader(Sam3ASession *ses) { - int res = aioLineReader(ses->fd, &ses->aio); - // - if (res < 0) { - sesError(ses, "IO_ERROR"); - return; - } - if (res > 0) { - // we got full line - if (libsam3a_debug) - fprintf(stderr, "CMDREPLY: %s\n", ses->aio.data); - if (ses->aio.cbReplyCheckSes != NULL) - ses->aio.cbReplyCheckSes(ses); - } -} - -static void aioSesCmdSender(Sam3ASession *ses) { - if (ses->aio.dataPos < ses->aio.dataUsed) { - if (aioSender(ses->fd, &ses->aio) < 0) { - sesError(ses, "IO_ERROR"); - return; - } - } - // - if (ses->aio.dataPos == ses->aio.dataUsed) { - // hello sent, now wait for reply - // 2048 bytes of reply line should be enough - if (ses->aio.dataSize < 2049) { - char *n = realloc(ses->aio.data, 2049); - // - if (n == NULL) { - sesError(ses, "MEMORY_ERROR"); - return; - } - ses->aio.data = n; - ses->aio.dataSize = 2049; - } - ses->aio.dataUsed = 2048; - ses->aio.dataPos = 0; - ses->cbAIOProcessorR = aioSesCmdReplyReader; - ses->cbAIOProcessorW = NULL; - } -} - -static __attribute__((format(printf, 3, 4))) int -aioSesSendCmdWaitReply(Sam3ASession *ses, void (*cbCheck)(Sam3ASession *ses), - const char *fmt, ...) { - va_list ap; - char *str; - int len; - // - va_start(ap, fmt); - str = sam3PrintfVA(&len, fmt, ap); - va_end(ap); - // - if (str == NULL) - return -1; - if (ses->aio.data != NULL) - free(ses->aio.data); - ses->aio.data = str; - ses->aio.dataUsed = len; - ses->aio.dataSize = len + 1; - ses->aio.dataPos = 0; - ses->aio.cbReplyCheckSes = cbCheck; - ses->cbAIOProcessorR = NULL; - ses->cbAIOProcessorW = aioSesCmdSender; - // - if (libsam3a_debug) - fprintf(stderr, "CMD: %s", str); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioSesHelloChecker(Sam3ASession *ses) { - SAMFieldList *rep = sam3aParseReply(ses->aio.data); - // - if (rep != NULL && sam3aIsGoodReply(rep, "HELLO", "REPLY", "RESULT", "OK") && - sam3aIsGoodReply(rep, NULL, NULL, "VERSION", "3.0")) { - ses->cbAIOProcessorR = ses->cbAIOProcessorW = NULL; - sam3aFreeFieldList(rep); - if (ses->aio.udata != NULL) { - void (*cbComplete)(Sam3ASession * ses) = ses->aio.udata; - // - cbComplete(ses); - } - } else { - sam3aFreeFieldList(rep); - sesError(ses, NULL); - } -} - -static int sam3aSesStartHandshake(Sam3ASession *ses, - void (*cbComplete)(Sam3ASession *ses)) { - if (cbComplete != NULL) - ses->aio.udata = cbComplete; - if (aioSesSendCmdWaitReply(ses, aioSesHelloChecker, "%s\n", - "HELLO VERSION MIN=3.0 MAX=3.0") < 0) - return -1; - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioSesNameMeChecker(Sam3ASession *ses) { - SAMFieldList *rep = sam3aParseReply(ses->aio.data); - const char *v = NULL; - // - if (rep == NULL) { - sesError(ses, NULL); - return; - } - if (!sam3aIsGoodReply(rep, "NAMING", "REPLY", "RESULT", "OK") || - (v = sam3aFindField(rep, "VALUE")) == NULL || - strlen(v) != SAM3A_PUBKEY_SIZE) { - // if (libsam3a_debug) fprintf(stderr, "sam3aCreateSession: invalid NAMING - // reply (%d)...\n", (v != NULL ? strlen(v) : -1)); - if ((v = sam3aFindField(rep, "RESULT")) != NULL && strcmp(v, "OK") == 0) - v = NULL; - sesError(ses, v); - sam3aFreeFieldList(rep); - return; - } - strcpy(ses->pubkey, v); - sam3aFreeFieldList(rep); - // - ses->cbAIOProcessorR = ses->cbAIOProcessorW = NULL; - ses->callDisconnectCB = 1; - if (ses->cb.cbCreated != NULL) - ses->cb.cbCreated(ses); -} - -static void aioSesCreateChecker(Sam3ASession *ses) { - SAMFieldList *rep = sam3aParseReply(ses->aio.data); - const char *v; - // - if (rep == NULL) { - sesError(ses, NULL); - return; - } - if (!sam3aIsGoodReply(rep, "SESSION", "STATUS", "RESULT", "OK") || - (v = sam3aFindField(rep, "DESTINATION")) == NULL || - strlen(v) != SAM3A_PRIVKEY_SIZE) { - sam3aFreeFieldList(rep); - if ((v = sam3aFindField(rep, "RESULT")) != NULL && strcmp(v, "OK") == 0) - v = NULL; - sesError(ses, v); - return; - } - // ok - // fprintf(stderr, "\nPK: %s\n", v); - strcpy(ses->privkey, v); - sam3aFreeFieldList(rep); - // get our public key - if (aioSesSendCmdWaitReply(ses, aioSesNameMeChecker, "%s\n", - "NAMING LOOKUP NAME=ME") < 0) { - sesError(ses, "MEMORY_ERROR"); - } -} - -// handshake for SESSION CREATE complete -static void aioSesHandshacked(Sam3ASession *ses) { - static const char *typenames[3] = {"RAW", "DATAGRAM", "STREAM"}; - // - if (aioSesSendCmdWaitReply( - ses, aioSesCreateChecker, - "SESSION CREATE STYLE=%s ID=%s DESTINATION=%s%s%s\n", - typenames[(int)ses->type], ses->channel, ses->privkey, - (ses->params != NULL ? " " : ""), - (ses->params != NULL ? ses->params : "")) < 0) { - sesError(ses, "MEMORY_ERROR"); - } -} - -static void aioSesConnected(Sam3ASession *ses) { - int res; - socklen_t len = sizeof(res); - // - if (getsockopt(ses->fd, SOL_SOCKET, SO_ERROR, &res, &len) == 0 && res == 0) { - // ok, connected - if (sam3aSesStartHandshake(ses, NULL) < 0) - sesError(ses, NULL); - } else { - // connection error - sesError(ses, "CONNECTION_ERROR"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aCreateSessionEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, - const char *hostname, int port, const char *privkey, - Sam3ASessionType type, const char *params, - int timeoutms) { - if (ses != NULL) { - // int complete = 0; - // - memset(ses, 0, sizeof(Sam3ASession)); - ses->fd = -1; - if (cb != NULL) - ses->cb = *cb; - if (hostname == NULL || !hostname[0]) - hostname = "127.0.0.1"; - if (port < 0 || port > 65535) - goto error; - if (privkey != NULL && strlen(privkey) != SAM3A_PRIVKEY_SIZE) - goto error; - if ((int)type < 0 || (int)type > 2) - goto error; - if (privkey == NULL) - privkey = "TRANSIENT"; - strcpy(ses->privkey, privkey); - if (params != NULL && (ses->params = strdup(params)) == NULL) - goto error; - ses->timeoutms = timeoutms; - // - if (!port) - port = DEFAULT_TCP_PORT; - ses->type = type; - ses->port = (type == SAM3A_SESSION_STREAM ? port : DEFAULT_UDP_PORT); - if ((ses->ip = sam3aResolveHost(hostname)) == 0) - goto error; - sam3aGenChannelName(ses->channel, 32, 64); - if (libsam3a_debug) - fprintf(stderr, "sam3aCreateSession: channel=[%s]\n", ses->channel); - // - ses->aio.udata = aioSesHandshacked; - ses->cbAIOProcessorW = aioSesConnected; - if ((ses->fd = sam3aConnect(ses->ip, port, NULL)) < 0) - goto error; - /* - if (complete) { - ses->cbAIOProcessorW(ses); - if (!sam3aIsActiveSession(ses)) return -1; - } - */ - // - return 0; // ok, connection process initiated - error: - if (ses->fd >= 0) - sam3aDisconnect(ses->fd); - if (ses->params != NULL) - free(ses->params); - memset(ses, 0, sizeof(Sam3ASession)); - ses->fd = -1; - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aCancelSession(Sam3ASession *ses) { - if (ses != NULL) { - sesDisconnect(ses); - return 0; - } - return -1; -} - -int sam3aCloseSession(Sam3ASession *ses) { - if (ses != NULL) { - sam3aCancelSession(ses); - while (ses->connlist != NULL) - sam3aCloseConnection(ses->connlist); - if (ses->cb.cbDestroy != NULL) - ses->cb.cbDestroy(ses); - if (ses->params != NULL) { - free(ses->params); - ses->params = NULL; - } - memset(ses, 0, sizeof(Sam3ASession)); - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioSesKeyGenChecker(Sam3ASession *ses) { - SAMFieldList *rep = sam3aParseReply(ses->aio.data); - // - if (rep == NULL) { - sesError(ses, NULL); - return; - } - if (sam3aIsGoodReply(rep, "DEST", "REPLY", NULL, NULL)) { - const char *pub = sam3aFindField(rep, "PUB"), - *priv = sam3aFindField(rep, "PRIV"); - // - if (pub != NULL && strlen(pub) == SAM3A_PUBKEY_SIZE && priv != NULL && - strlen(priv) == SAM3A_PRIVKEY_SIZE) { - strcpy(ses->pubkey, pub); - strcpy(ses->privkey, priv); - sam3aFreeFieldList(rep); - if (ses->cb.cbCreated != NULL) - ses->cb.cbCreated(ses); - sam3aCancelSession(ses); - return; - } - } - sam3aFreeFieldList(rep); - sesError(ses, NULL); -} - -// handshake for SESSION CREATE complete -static void aioSesKeyGenHandshacked(Sam3ASession *ses) { - if (aioSesSendCmdWaitReply(ses, aioSesKeyGenChecker, "%s\n", - "DEST GENERATE") < 0) { - sesError(ses, "MEMORY_ERROR"); - } -} - -int sam3aGenerateKeysEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, - const char *hostname, int port, int timeoutms) { - if (ses != NULL) { - memset(ses, 0, sizeof(Sam3ASession)); - ses->fd = -1; - if (cb != NULL) - ses->cb = *cb; - if (hostname == NULL || !hostname[0]) - hostname = "127.0.0.1"; - if (port < 0 || port > 65535) - goto error; - ses->timeoutms = timeoutms; - // - if (!port) - port = DEFAULT_TCP_PORT; - ses->port = port; - if ((ses->ip = sam3aResolveHost(hostname)) == 0) - goto error; - // - ses->aio.udata = aioSesKeyGenHandshacked; - ses->cbAIOProcessorW = aioSesConnected; - if ((ses->fd = sam3aConnect(ses->ip, port, NULL)) < 0) - goto error; - // - return 0; // ok, connection process initiated - error: - if (ses->fd >= 0) - sam3aDisconnect(ses->fd); - if (ses->params != NULL) - free(ses->params); - memset(ses, 0, sizeof(Sam3ASession)); - ses->fd = -1; - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioSesNameResChecker(Sam3ASession *ses) { - SAMFieldList *rep = sam3aParseReply(ses->aio.data); - // - if (rep == NULL) { - sesError(ses, NULL); - return; - } - if (sam3aIsGoodReply(rep, "NAMING", "REPLY", "RESULT", NULL)) { - const char *rs = sam3aFindField(rep, "RESULT"), - *pub = sam3aFindField(rep, "VALUE"); - // - if (strcmp(rs, "OK") == 0) { - if (pub != NULL && strlen(pub) == SAM3A_PUBKEY_SIZE) { - strcpy(ses->destkey, pub); - sam3aFreeFieldList(rep); - if (ses->cb.cbCreated != NULL) - ses->cb.cbCreated(ses); - sam3aCancelSession(ses); - return; - } - sam3aFreeFieldList(rep); - sesError(ses, NULL); - } else { - sesError(ses, rs); - sam3aFreeFieldList(rep); - } - } -} - -// handshake for SESSION CREATE complete -static void aioSesNameResHandshacked(Sam3ASession *ses) { - if (aioSesSendCmdWaitReply(ses, aioSesNameResChecker, - "NAMING LOOKUP NAME=%s\n", ses->params) < 0) { - sesError(ses, "MEMORY_ERROR"); - } -} - -int sam3aNameLookupEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, - const char *hostname, int port, const char *name, - int timeoutms) { - if (ses != NULL) { - memset(ses, 0, sizeof(Sam3ASession)); - ses->fd = -1; - if (cb != NULL) - ses->cb = *cb; - if (name == NULL || !name[0] || - (name[0] && toupper(name[0]) == 'M' && name[1] && - toupper(name[1]) == 'E' && (!name[2] || isspace(name[2])))) - goto error; - if (hostname == NULL || !hostname[0]) - hostname = "127.0.0.1"; - if (port < 0 || port > 65535) - goto error; - if ((ses->params = strdup(name)) == NULL) - goto error; - ses->timeoutms = timeoutms; - // - if (!port) - port = DEFAULT_TCP_PORT; - ses->port = port; - if ((ses->ip = sam3aResolveHost(hostname)) == 0) - goto error; - // - ses->aio.udata = aioSesNameResHandshacked; - ses->cbAIOProcessorW = aioSesConnected; - if ((ses->fd = sam3aConnect(ses->ip, port, NULL)) < 0) - goto error; - // - return 0; // ok, connection process initiated - error: - if (ses->fd >= 0) - sam3aDisconnect(ses->fd); - if (ses->params != NULL) - free(ses->params); - memset(ses, 0, sizeof(Sam3ASession)); - ses->fd = -1; - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioConnCmdReplyReader(Sam3AConnection *conn) { - int res = aioLineReader(conn->fd, &conn->aio); - // - if (res < 0) { - connError(conn, "IO_ERROR"); - return; - } - if (res > 0) { - // we got full line - if (libsam3a_debug) - fprintf(stderr, "CMDREPLY: %s\n", conn->aio.data); - if (conn->aio.cbReplyCheckConn != NULL) - conn->aio.cbReplyCheckConn(conn); - } -} - -static void aioConnCmdSender(Sam3AConnection *conn) { - if (conn->aio.dataPos < conn->aio.dataUsed) { - if (aioSender(conn->fd, &conn->aio) < 0) { - connError(conn, "IO_ERROR"); - return; - } - } - // - if (conn->aio.dataPos == conn->aio.dataUsed) { - // hello sent, now wait for reply - // 2048 bytes of reply line should be enough - if (conn->aio.dataSize < 2049) { - char *n = realloc(conn->aio.data, 2049); - // - if (n == NULL) { - connError(conn, "MEMORY_ERROR"); - return; - } - conn->aio.data = n; - conn->aio.dataSize = 2049; - } - conn->aio.dataUsed = 2048; - conn->aio.dataPos = 0; - conn->cbAIOProcessorR = aioConnCmdReplyReader; - conn->cbAIOProcessorW = NULL; - } -} - -static __attribute__((format(printf, 3, 4))) int -aioConnSendCmdWaitReply(Sam3AConnection *conn, - void (*cbCheck)(Sam3AConnection *conn), const char *fmt, - ...) { - va_list ap; - char *str; - int len; - // - va_start(ap, fmt); - str = sam3PrintfVA(&len, fmt, ap); - va_end(ap); - // - if (str == NULL) - return -1; - if (conn->aio.data != NULL) - free(conn->aio.data); - conn->aio.data = str; - conn->aio.dataUsed = len; - conn->aio.dataSize = len + 1; - conn->aio.dataPos = 0; - conn->aio.cbReplyCheckConn = cbCheck; - conn->cbAIOProcessorR = NULL; - conn->cbAIOProcessorW = aioConnCmdSender; - // - if (libsam3a_debug) - fprintf(stderr, "CMD: %s", str); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioConnDataReader(Sam3AConnection *conn) { - char *buf = NULL; - int bufsz = 0; - // - while (sam3aIsActiveConnection(conn)) { - int av = sam3aBytesAvail(conn->fd), rd; - // - if (av < 0) { - if (buf != NULL) - free(buf); - connError(conn, "IO_ERROR"); - return; - } - if (av == 0) - av = 1; - if (bufsz < av) { - char *n = realloc(buf, av + 1); - // - if (n == NULL) { - if (buf != NULL) - free(buf); - connError(conn, "IO_ERROR"); - return; - } - buf = n; - bufsz = av; - } - memset(buf, 0, av + 1); - // - rd = recv(conn->fd, buf, av, 0); - // - if (rd < 0) { - if (errno == EINTR) - continue; // interrupted by signal - if (errno == EAGAIN || errno == EWOULDBLOCK) - break; // no more data - free(buf); - connError(conn, "IO_ERROR"); - return; - } - // - if (rd == 0) { - // connection closed - free(buf); - connDisconnect(conn); - return; - } - // - if (conn->cb.cbRead != NULL) - conn->cb.cbRead(conn, buf, rd); - } - free(buf); -} - -static void aioConnDataWriter(Sam3AConnection *conn) { - if (!sam3aIsActiveConnection(conn)) { - conn->aio.dataPos = conn->aio.dataUsed = 0; - return; - } - // - if (conn->aio.dataPos >= conn->aio.dataUsed) { - conn->aio.dataPos = conn->aio.dataUsed = 0; - return; - } - // - while (sam3aIsActiveConnection(conn) && - conn->aio.dataPos < conn->aio.dataUsed) { - int wr = sam3aSendBytes(conn->fd, conn->aio.data + conn->aio.dataPos, - conn->aio.dataUsed - conn->aio.dataPos); - // - if (wr < 0) { - connError(conn, "IO_ERROR"); - return; - } - if (wr == 0) - break; // can't write more bytes - conn->aio.dataPos += wr; - if (conn->aio.dataPos < conn->aio.dataUsed) { - memmove(conn->aio.data, conn->aio.data + conn->aio.dataPos, - conn->aio.dataUsed - conn->aio.dataPos); - conn->aio.dataUsed -= conn->aio.dataPos; - conn->aio.dataPos = 0; - } - } - // - if (conn->aio.dataPos >= conn->aio.dataUsed) { - conn->aio.dataPos = conn->aio.dataUsed = 0; - if (conn->cb.cbSent != NULL) - conn->cb.cbSent(conn); - if (conn->aio.dataSize > 8192) { - // shrink buffer - char *nn = realloc(conn->aio.data, 8192); - // - if (nn != NULL) { - conn->aio.data = nn; - conn->aio.dataSize = 8192; - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioConnHelloChecker(Sam3AConnection *conn) { - SAMFieldList *rep = sam3aParseReply(conn->aio.data); - // - if (rep != NULL && sam3aIsGoodReply(rep, "HELLO", "REPLY", "RESULT", "OK") && - sam3aIsGoodReply(rep, NULL, NULL, "VERSION", "3.0")) { - conn->cbAIOProcessorR = conn->cbAIOProcessorW = NULL; - sam3aFreeFieldList(rep); - if (conn->aio.udata != NULL) { - void (*cbComplete)(Sam3AConnection * conn) = conn->aio.udata; - // - cbComplete(conn); - } - } else { - sam3aFreeFieldList(rep); - connError(conn, NULL); - } -} - -static int sam3aConnStartHandshake(Sam3AConnection *conn, - void (*cbComplete)(Sam3AConnection *conn)) { - if (cbComplete != NULL) - conn->aio.udata = cbComplete; - if (aioConnSendCmdWaitReply(conn, aioConnHelloChecker, "%s\n", - "HELLO VERSION MIN=3.0 MAX=3.0") < 0) - return -1; - return 0; -} - -static void aioConnConnected(Sam3AConnection *conn) { - int res; - socklen_t len = sizeof(res); - // - if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &res, &len) == 0 && res == 0) { - // ok, connected - if (sam3aConnStartHandshake(conn, NULL) < 0) - connError(conn, NULL); - } else { - // connection error - connError(conn, "CONNECTION_ERROR"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioConnConnectChecker(Sam3AConnection *conn) { - SAMFieldList *rep = sam3aParseReply(conn->aio.data); - // - if (rep == NULL) { - connError(conn, NULL); - return; - } - if (!sam3aIsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { - const char *v = sam3aFindField(rep, "RESULT"); - // - connError(conn, v); - sam3aFreeFieldList(rep); - } else { - // no error - sam3aFreeFieldList(rep); - conn->callDisconnectCB = 1; - conn->cbAIOProcessorR = aioConnDataReader; - conn->cbAIOProcessorW = aioConnDataWriter; - conn->aio.dataPos = conn->aio.dataUsed = 0; - if (conn->cb.cbConnected != NULL) - conn->cb.cbConnected(conn); - // indicate that we are ready for new data - if (sam3aIsActiveConnection(conn) && conn->cb.cbSent != NULL) - conn->cb.cbSent(conn); - } -} - -// handshake for SESSION CREATE complete -static void aioConConnectHandshacked(Sam3AConnection *conn) { - if (aioConnSendCmdWaitReply(conn, aioConnConnectChecker, - "STREAM CONNECT ID=%s DESTINATION=%s\n", - conn->ses->channel, conn->destkey) < 0) { - connError(conn, "MEMORY_ERROR"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -Sam3AConnection *sam3aStreamConnectEx(Sam3ASession *ses, - const Sam3AConnectionCallbacks *cb, - const char *destkey, int timeoutms) { - if (sam3aIsActiveSession(ses) && ses->type == SAM3A_SESSION_STREAM && - destkey != NULL && strlen(destkey) == SAM3A_PUBKEY_SIZE) { - Sam3AConnection *conn = calloc(1, sizeof(Sam3AConnection)); - // - if (conn == NULL) - return NULL; - if (cb != NULL) - conn->cb = *cb; - strcpy(conn->destkey, destkey); - conn->timeoutms = timeoutms; - // - conn->aio.udata = aioConConnectHandshacked; - conn->cbAIOProcessorW = aioConnConnected; - if ((conn->fd = sam3aConnect(ses->ip, ses->port, NULL)) < 0) - goto error; - // - conn->ses = ses; - conn->next = ses->connlist; - ses->connlist = conn; - return conn; // ok, connection process initiated - error: - if (conn->fd >= 0) - sam3aDisconnect(conn->fd); - memset(conn, 0, sizeof(Sam3AConnection)); - free(conn); - } - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -static void aioConnAcceptCheckerA(Sam3AConnection *conn) { - SAMFieldList *rep = sam3aParseReply(conn->aio.data); - // - if (rep != NULL || strlen(conn->aio.data) != SAM3A_PUBKEY_SIZE || - !sam3aIsValidPubKey(conn->aio.data)) { - sam3aFreeFieldList(rep); - connError(conn, NULL); - return; - } - sam3aFreeFieldList(rep); - strcpy(conn->destkey, conn->aio.data); - conn->callDisconnectCB = 1; - conn->cbAIOProcessorR = aioConnDataReader; - conn->cbAIOProcessorW = aioConnDataWriter; - conn->aio.dataPos = conn->aio.dataUsed = 0; - if (conn->cb.cbAccepted != NULL) - conn->cb.cbAccepted(conn); - // indicate that we are ready for new data - if (sam3aIsActiveConnection(conn) && conn->cb.cbSent != NULL) - conn->cb.cbSent(conn); -} - -static void aioConnAcceptChecker(Sam3AConnection *conn) { - SAMFieldList *rep = sam3aParseReply(conn->aio.data); - // - if (rep == NULL) { - connError(conn, NULL); - return; - } - if (!sam3aIsGoodReply(rep, "STREAM", "STATUS", "RESULT", "OK")) { - const char *v = sam3aFindField(rep, "RESULT"); - // - connError(conn, v); - sam3aFreeFieldList(rep); - } else { - // no error - sam3aFreeFieldList(rep); - // 2048 bytes of reply line should be enough - if (conn->aio.dataSize < 2049) { - char *n = realloc(conn->aio.data, 2049); - // - if (n == NULL) { - connError(conn, "MEMORY_ERROR"); - return; - } - conn->aio.data = n; - conn->aio.dataSize = 2049; - } - conn->aio.dataUsed = 2048; - conn->aio.dataPos = 0; - conn->cbAIOProcessorR = aioConnCmdReplyReader; - conn->cbAIOProcessorW = NULL; - conn->aio.cbReplyCheckConn = aioConnAcceptCheckerA; - } -} - -// handshake for SESSION CREATE complete -static void aioConAcceptHandshacked(Sam3AConnection *conn) { - if (aioConnSendCmdWaitReply(conn, aioConnAcceptChecker, - "STREAM ACCEPT ID=%s\n", - conn->ses->channel) < 0) { - connError(conn, "MEMORY_ERROR"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -Sam3AConnection *sam3aStreamAcceptEx(Sam3ASession *ses, - const Sam3AConnectionCallbacks *cb, - int timeoutms) { - if (sam3aIsActiveSession(ses) && ses->type == SAM3A_SESSION_STREAM) { - Sam3AConnection *conn = calloc(1, sizeof(Sam3AConnection)); - // - if (conn == NULL) - return NULL; - if (cb != NULL) - conn->cb = *cb; - conn->timeoutms = timeoutms; - // - conn->aio.udata = aioConAcceptHandshacked; - conn->cbAIOProcessorW = aioConnConnected; - if ((conn->fd = sam3aConnect(ses->ip, ses->port, NULL)) < 0) - goto error; - // - conn->ses = ses; - conn->next = ses->connlist; - ses->connlist = conn; - return conn; // ok, connection process initiated - error: - if (conn->fd >= 0) - sam3aDisconnect(conn->fd); - memset(conn, 0, sizeof(Sam3AConnection)); - free(conn); - } - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aSend(Sam3AConnection *conn, const void *data, int datasize) { - if (datasize == -1) - datasize = (data != NULL ? strlen((const char *)data) : 0); - // - if (sam3aIsActiveConnection(conn) && conn->callDisconnectCB && - conn->cbAIOProcessorW != NULL && - ((datasize > 0 && data != NULL) || datasize == 0)) { - // try to add data to send buffer - if (datasize > 0) { - if (conn->aio.dataUsed + datasize > conn->aio.dataSize) { - // we need more pepper! - int newsz = conn->aio.dataUsed + datasize; - char *nb = realloc(conn->aio.data, newsz); - // - if (nb == NULL) - return -1; // alas - conn->aio.data = nb; - conn->aio.dataSize = newsz; - } - // - memcpy(conn->aio.data + conn->aio.dataUsed, data, datasize); - conn->aio.dataUsed += datasize; - } - return 0; - } - // - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aIsHaveActiveConnections(const Sam3ASession *ses) { - if (sam3aIsActiveSession(ses)) { - for (const Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) { - if (sam3aIsActiveConnection(c)) - return 1; - } - } - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aCancelConnection(Sam3AConnection *conn) { - if (conn != NULL) { - connDisconnect(conn); - return 0; - } - return -1; -} - -int sam3aCloseConnection(Sam3AConnection *conn) { - if (conn != NULL) { - sam3aCancelConnection(conn); - if (conn->cb.cbDestroy != NULL) - conn->cb.cbDestroy(conn); - for (Sam3AConnection *p = NULL, *c = conn->ses->connlist; c != NULL; - p = c, c = c->next) { - if (c == conn) { - // got it! - if (p == NULL) - c->ses->connlist = c->next; - else - p->next = c->next; - break; - } - } - if (conn->params != NULL) { - free(conn->params); - conn->params = NULL; - } - memset(conn, 0, sizeof(Sam3AConnection)); - free(conn); - } - return -1; -} - -//////////////////////////////////////////////////////////////////////////////// -int sam3aAddSessionToFDS(Sam3ASession *ses, int maxfd, fd_set *rds, - fd_set *wrs) { - if (ses != NULL) { - if (sam3aIsActiveSession(ses)) { - if (rds != NULL && ses->cbAIOProcessorR != NULL) { - if (maxfd < ses->fd) - maxfd = ses->fd; - FD_SET(ses->fd, rds); - } - // - if (wrs != NULL && ses->cbAIOProcessorW != NULL) { - if (maxfd < ses->fd) - maxfd = ses->fd; - FD_SET(ses->fd, wrs); - } - // - for (Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) { - if (sam3aIsActiveConnection(c)) { - if (rds != NULL && c->cbAIOProcessorR != NULL) { - if (maxfd < c->fd) - maxfd = c->fd; - FD_SET(c->fd, rds); - } - // - if (wrs != NULL && c->cbAIOProcessorW != NULL) { - if (!c->callDisconnectCB || (c->aio.dataPos < c->aio.dataUsed)) - if (maxfd < c->fd) - maxfd = c->fd; - FD_SET(c->fd, wrs); - } - } - } - } - return maxfd; - } - // - return -1; -} - -void sam3aProcessSessionIO(Sam3ASession *ses, fd_set *rds, fd_set *wrs) { - if (sam3aIsActiveSession(ses)) { - if (ses->fd >= 0 && !ses->cancelled && ses->cbAIOProcessorR != NULL && - rds != NULL && FD_ISSET(ses->fd, rds)) - ses->cbAIOProcessorR(ses); - if (ses->fd >= 0 && !ses->cancelled && ses->cbAIOProcessorW != NULL && - wrs != NULL && FD_ISSET(ses->fd, wrs)) - ses->cbAIOProcessorW(ses); - // - for (Sam3AConnection *c = ses->connlist; c != NULL; c = c->next) { - if (c->fd >= 0 && !c->cancelled && c->cbAIOProcessorR != NULL && - rds != NULL && FD_ISSET(c->fd, rds)) - c->cbAIOProcessorR(c); - if (c->fd >= 0 && !c->cancelled && c->cbAIOProcessorW != NULL && - wrs != NULL && FD_ISSET(c->fd, wrs)) - c->cbAIOProcessorW(c); - } - } -} diff --git a/supportlibs/libsam3/src/libsam3a/libsam3a.h b/supportlibs/libsam3/src/libsam3a/libsam3a.h deleted file mode 100644 index dfd4c0cba..000000000 --- a/supportlibs/libsam3/src/libsam3a/libsam3a.h +++ /dev/null @@ -1,372 +0,0 @@ -/* async SAMv3 library - * - * This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#ifndef LIBSAM3A_H -#define LIBSAM3A_H - -#include -#include -#include - -#include - -#ifdef __MINGW32__ -//#include -#include -#include -#include -//#define SOCK_CLOEXEC O_CLOEXEC -//#define SOCK_NONBLOCK O_NONBLOCK -#define SOCK_CLOEXEC 02000000 -#define SOCK_NONBLOCK FIONBIO -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -//////////////////////////////////////////////////////////////////////////////// -/* - * TODO: - * [.] block sam3aClose*() in callbacks - */ - -//////////////////////////////////////////////////////////////////////////////// -extern int libsam3a_debug; - -//////////////////////////////////////////////////////////////////////////////// -#define SAM3A_HOST_DEFAULT (NULL) -#define SAM3A_PORT_DEFAULT (0) - -#define SAM3A_DESTINATION_TRANSIENT (NULL) - -#define SAM3A_PUBKEY_SIZE (516) -#define SAM3A_PRIVKEY_SIZE (884) - -//////////////////////////////////////////////////////////////////////////////// -extern uint64_t sam3atimeval2ms(const struct timeval *tv); -extern void sam3ams2timeval(struct timeval *tv, uint64_t ms); - -//////////////////////////////////////////////////////////////////////////////// -extern int sam3aIsValidPubKey(const char *key); -extern int sam3aIsValidPrivKey(const char *key); - -//////////////////////////////////////////////////////////////////////////////// -typedef struct Sam3ASession Sam3ASession; -typedef struct Sam3AConnection Sam3AConnection; - -typedef enum { - SAM3A_SESSION_RAW, - SAM3A_SESSION_DGRAM, - SAM3A_SESSION_STREAM -} Sam3ASessionType; - -typedef struct { - char *data; - int dataSize; - int dataUsed; - int dataPos; - void *udata; - union { - void (*cbReplyCheckSes)(Sam3ASession *ses); - void (*cbReplyCheckConn)(Sam3AConnection *conn); - }; -} Sam3AIO; - -/** session callback functions */ -typedef struct { - void (*cbError)(Sam3ASession *ses); /** called on error */ - void (*cbCreated)( - Sam3ASession *ses); /** called when we created the session */ - void (*cbDisconnected)(Sam3ASession *ses); /* call when closed; will called - only after cbCreated() */ - void (*cbDatagramRead)(Sam3ASession *ses, const void *buf, - int bufsize); /* called when we got a datagram; bufsize - >= 0; destkey set */ - void (*cbDestroy)(Sam3ASession *ses); /* called when fd is already closed, but - keys is not cleared */ -} Sam3ASessionCallbacks; - -struct Sam3ASession { - Sam3ASessionType type; /** session type */ - int fd; /** socket file descriptor */ - int cancelled; /** fd was shutdown()ed, but not closed yet */ - char privkey[SAM3A_PRIVKEY_SIZE + 1]; /** private key (asciiz) */ - char pubkey[SAM3A_PUBKEY_SIZE + 1]; /** public key (asciiz) */ - char channel[66]; /** channel name (asciiz) */ - char destkey[SAM3A_PUBKEY_SIZE + 1]; /** for DGRAM sessions (asciiz) */ - char error[64]; /** error message (asciiz) */ - uint32_t ip; /** ipv4 address of sam api interface */ - int port; /** UDP port for DRAM/RAW (can be 0) */ - Sam3AConnection *connlist; /** list of opened connections */ - - /** begin internal members */ - // for async i/o - Sam3AIO aio; - void (*cbAIOProcessorR)(Sam3ASession *ses); // internal - void (*cbAIOProcessorW)(Sam3ASession *ses); // internal - int callDisconnectCB; - char *params; // will be cleared only by sam3aCloseSession() - int timeoutms; - - /** end internal members */ - - Sam3ASessionCallbacks cb; - void *udata; -}; - -/** connection callbacks for data sockets */ -typedef struct { - /** called on error */ - void (*cbError)(Sam3AConnection *ct); - /** called when closed or only after cbConnected()/cbAccepted(); note that - * force disconnect is ok */ - void (*cbDisconnected)(Sam3AConnection *ct); - /** called when connected */ - void (*cbConnected)(Sam3AConnection *ct); - /** called instead of cbConnected() for sam3aStreamAccept*(), destkey filled - * with remote destination */ - void (*cbAccepted)(Sam3AConnection *ct); - /** send callback, data sent, can add new data; will be called after - * connect/accept */ - void (*cbSent)(Sam3AConnection *ct); - /** read callback, data read from socket (bufsize is always > 0) */ - void (*cbRead)(Sam3AConnection *ct, const void *buf, int bufsize); - /** fd already closed, but keys is not cleared */ - void (*cbDestroy)(Sam3AConnection *ct); -} Sam3AConnectionCallbacks; - -struct Sam3AConnection { - /** parent session */ - Sam3ASession *ses; - Sam3AConnection *next; - /** file descriptor */ - int fd; - int cancelled; // fd was shutdown()ed, but not closed yet - char destkey[SAM3A_PUBKEY_SIZE + 1]; // (asciiz) - char error[32]; // (asciiz) - - /** begin internal members */ - // for async i/o - Sam3AIO aio; - void (*cbAIOProcessorR)(Sam3AConnection *ct); // internal - void (*cbAIOProcessorW)(Sam3AConnection *ct); // internal - int callDisconnectCB; - char *params; // will be cleared only by sam3aCloseConnection() - int timeoutms; - /** end internal members */ - - /** callbacks */ - Sam3AConnectionCallbacks cb; - /** user data */ - void *udata; -}; - -//////////////////////////////////////////////////////////////////////////////// -/* - * check if session is active (i.e. have opened socket) - * returns bool - */ -extern int sam3aIsActiveSession(const Sam3ASession *ses); - -/* - * check if connection is active (i.e. have opened socket) - * returns bool - */ -extern int sam3aIsActiveConnection(const Sam3AConnection *conn); - -//////////////////////////////////////////////////////////////////////////////// -/* - * note, that return error codes indicates invalid structure, pointer or fd - * (i.e. immediate errors); all network errors indicated with cbError() callback - */ - -/* - * create SAM session - * pass NULL as hostname for 'localhost' and 0 as port for 7656 - * pass NULL as privkey to create TRANSIENT session - * 'params' can be NULL - * see http://www.i2p2.i2p/i2cp.html#options for common options, - * and http://www.i2p2.i2p/streaming.html#options for STREAM options - * if result<0: error, 'ses' fields are undefined, no need to call - * sam3aCloseSession() if result==0: ok, all 'ses' fields are filled - * TODO: don't clear 'error' field on error (and set it to something meaningful) - */ -extern int sam3aCreateSessionEx(Sam3ASession *ses, - const Sam3ASessionCallbacks *cb, - const char *hostname, int port, - const char *privkey, Sam3ASessionType type, - const char *params, int timeoutms); - -static inline int sam3aCreateSession(Sam3ASession *ses, - const Sam3ASessionCallbacks *cb, - const char *hostname, int port, - const char *privkey, - Sam3ASessionType type) { - return sam3aCreateSessionEx(ses, cb, hostname, port, privkey, type, NULL, -1); -} - -/* returns <0 on error, 0 if no, >0 if yes */ -extern int sam3aIsHaveActiveConnections(const Sam3ASession *ses); - -/* - * close SAM session (and all it's connections) - * returns <0 on error, 0 on ok - * 'ses' must be properly initialized - */ -extern int sam3aCloseSession(Sam3ASession *ses); - -/* - * cancel SAM session (and all it's connections), but don't free() or clear - * anything except fds returns <0 on error, 0 on ok 'ses' must be properly - * initialized - */ -extern int sam3aCancelSession(Sam3ASession *ses); - -/* - * open stream connection to 'destkey' endpoint - * 'destkey' is 516-byte public key (asciiz) - * returns <0 on error - * sets ses->error on memory or socket creation error - */ -extern Sam3AConnection *sam3aStreamConnectEx(Sam3ASession *ses, - const Sam3AConnectionCallbacks *cb, - const char *destkey, - int timeoutms); - -static inline Sam3AConnection * -sam3aStreamConnect(Sam3ASession *ses, const Sam3AConnectionCallbacks *cb, - const char *destkey) { - return sam3aStreamConnectEx(ses, cb, destkey, -1); -} - -/* - * accepts stream connection and sets 'destkey' - * 'destkey' is 516-byte public key - * returns <0 on error, fd on ok - * you still have to call sam3aCloseSession() on failure - * sets ses->error on error - * note that there is no timeouts for now, but you can use sam3atcpSetTimeout*() - */ -extern Sam3AConnection *sam3aStreamAcceptEx(Sam3ASession *ses, - const Sam3AConnectionCallbacks *cb, - int timeoutms); - -static inline Sam3AConnection * -sam3aStreamAccept(Sam3ASession *ses, const Sam3AConnectionCallbacks *cb) { - return sam3aStreamAcceptEx(ses, cb, -1); -} - -/* - * close SAM connection, remove it from session and free memory - * returns <0 on error, 0 on ok - * 'conn' must be properly initialized - * 'conn' is invalid after call - */ -extern int sam3aCloseConnection(Sam3AConnection *conn); - -/* - * cancel SAM connection, but don't free() or clear anything except fd - * returns <0 on error, 0 on ok - * 'conn' must be properly initialized - * 'conn' is invalid after call - */ -extern int sam3aCancelConnection(Sam3AConnection *conn); - -//////////////////////////////////////////////////////////////////////////////// -/* - * send data - * this function can be used in cbSent() callback - * - * return: <0: error; 0: ok - */ -extern int sam3aSend(Sam3AConnection *conn, const void *data, int datasize); - -/* - * sends datagram to 'destkey' endpoint - * 'destkey' is 516-byte public key - * returns <0 on error, 0 on ok - * you still have to call sam3aCloseSession() on failure - * sets ses->error on error - * don't send datagrams bigger than 31KB! - */ -extern int sam3aDatagramSend(Sam3ASession *ses, const char *destkey, - const void *buf, int bufsize); - -//////////////////////////////////////////////////////////////////////////////// -/* - * generate random channel name - * dest should be at least (maxlen+1) bytes big - */ -extern int sam3aGenChannelName(char *dest, int minlen, int maxlen); - -//////////////////////////////////////////////////////////////////////////////// -/* - * generate new keypair - * fills 'privkey' and 'pubkey' only - * you should call sam3aCloseSession() on 'ses' - * cbCreated callback will be called when keys generated - * returns <0 on error, 0 on ok - */ -extern int sam3aGenerateKeysEx(Sam3ASession *ses, - const Sam3ASessionCallbacks *cb, - const char *hostname, int port, int timeoutms); - -static inline int sam3aGenerateKeys(Sam3ASession *ses, - const Sam3ASessionCallbacks *cb, - const char *hostname, int port) { - return sam3aGenerateKeysEx(ses, cb, hostname, port, -1); -} - -/* - * do name lookup (something like gethostbyname()) - * fills 'destkey' only - * you should call sam3aCloseSession() on 'ses' - * cbCreated callback will be called when keys generated, ses->destkey will be - * set returns <0 on error, 0 on ok - */ -extern int sam3aNameLookupEx(Sam3ASession *ses, const Sam3ASessionCallbacks *cb, - const char *hostname, int port, const char *name, - int timeoutms); - -static inline int sam3aNameLookup(Sam3ASession *ses, - const Sam3ASessionCallbacks *cb, - const char *hostname, int port, - const char *name) { - return sam3aNameLookupEx(ses, cb, hostname, port, name, -1); -} - -//////////////////////////////////////////////////////////////////////////////// -/* - * append session fd to read and write sets if necessary - * adds all alive session connections too - * returns maxfd or -1 - * TODO: should keep fd count so it will not exceed FD_SETSIZE! - */ -extern int sam3aAddSessionToFDS(Sam3ASession *ses, int maxfd, fd_set *rds, - fd_set *wrs); - -/* - * process session i/o (and all session connections i/o) - * should be called after successful select() - */ -extern void sam3aProcessSessionIO(Sam3ASession *ses, fd_set *rds, fd_set *wrs); - -//////////////////////////////////////////////////////////////////////////////// -/* return malloc()ed buffer and len in 'plen' (if plen != NULL) */ -extern char *sam3PrintfVA(int *plen, const char *fmt, va_list app); -extern char *sam3Printf(int *plen, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/supportlibs/libsam3/test/libsam3/test_b32.c b/supportlibs/libsam3/test/libsam3/test_b32.c deleted file mode 100644 index 9a852853d..000000000 --- a/supportlibs/libsam3/test/libsam3/test_b32.c +++ /dev/null @@ -1,51 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://sam.zoy.org/wtfpl/COPYING for more details. - * - * I2P-Bote: - * 5m77dFKGEq6~7jgtrfw56q3t~SmfwZubmGdyOLQOPoPp8MYwsZ~pfUCwud6LB1EmFxkm4C3CGlzq-hVs9WnhUV - * we are the Borg. */ -#include -#include -#include -#include - -#include "../../src/ext/tinytest.h" -#include "../../src/ext/tinytest_macros.h" -#include "../../src/libsam3/libsam3.h" - -static int testb32(const char *src, const char *res) { - size_t dlen = sam3Base32EncodedLength(strlen(src)), len; - char dest[128]; - // - len = sam3Base32Encode(dest, sizeof(dest), src, strlen(src)); - tt_int_op(len, ==, dlen); - tt_int_op(len, ==, strlen(res)); - tt_str_op(res, ==, dest); - return 1; - -end: - return 0; -} - -void test_b32_encode(void *data) { - (void)data; /* This testcase takes no data. */ - - tt_assert(testb32("", "")); - tt_assert(testb32("f", "my======")); - tt_assert(testb32("fo", "mzxq====")); - tt_assert(testb32("foo", "mzxw6===")); - tt_assert(testb32("foob", "mzxw6yq=")); - tt_assert(testb32("fooba", "mzxw6ytb")); - tt_assert(testb32("foobar", "mzxw6ytboi======")); - -end:; -} - -struct testcase_t b32_tests[] = {{ - "encode", - test_b32_encode, - }, - END_OF_TESTCASES}; diff --git a/supportlibs/libsam3/test/test.c b/supportlibs/libsam3/test/test.c deleted file mode 100644 index 9e7357962..000000000 --- a/supportlibs/libsam3/test/test.c +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "../src/ext/tinytest.h" -#include "../src/ext/tinytest_macros.h" - -extern struct testcase_t b32_tests[]; - -struct testgroup_t test_groups[] = {{"b32/", b32_tests}, END_OF_GROUPS}; - -int main(int argc, const char **argv) { - return tinytest_main(argc, argv, test_groups); -} From 9c71177d3b6c8f8072e620b275e682277bc9f3ec Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 27 Nov 2020 17:09:02 +0100 Subject: [PATCH 13/20] add workaround for i2p/libsam3#15 --- libretroshare/src/services/autoproxy/p3i2psam3.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp index 47123328a..a708517e2 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.cpp +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -616,9 +616,17 @@ bool p3I2pSam3::generateKey(std::string &pub, std::string &priv) RS_DBG("got error: ", ss.error); return false; } + pub = std::string(ss.pubkey); priv = std::string(ss.privkey); + // sanity check + auto p = i2p::publicKeyFromPrivate(priv); + if (p != pub) { + RS_WARN("public key does not match private key! fixing ..."); + pub = p; + } + RS_DBG2("publuc key / address ", pub); RS_DBG2("private key ", priv); From 8a2efe3e586c63a5d3618fb913da31d37970a3fe Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 27 Nov 2020 20:07:51 +0100 Subject: [PATCH 14/20] use libsam3 git submodule --- .gitmodules | 3 +++ libretroshare/src/libretroshare.pro | 5 ++++- supportlibs/libsam3 | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) create mode 160000 supportlibs/libsam3 diff --git a/.gitmodules b/.gitmodules index c35876fc6..2f69bb7b2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,6 @@ [submodule "supportlibs/rapidjson"] path = supportlibs/rapidjson url = https://github.com/Tencent/rapidjson.git +[submodule "supportlibs/libsam3"] + path = supportlibs/libsam3 + url = https://github.com/i2p/libsam3.git diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index 0a8ba44a7..40183772a 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -1029,7 +1029,10 @@ rs_sam3_libsam3 { libsam3.CONFIG += target_predeps combine libsam3.variable_out = PRE_TARGETDEPS libsam3.commands = \ - cd $${RS_SRC_PATH} && \ + cd $${RS_SRC_PATH} && ( \ + git submodule update --init supportlibs/libsam3 || \ + true ) && \ + mkdir -p $${UDP_DISCOVERY_BUILD_PATH} && \ cp -r $${LIBSAM3_SRC_PATH}/* $${LIBSAM3_BUILD_PATH} && \ cd $${LIBSAM3_BUILD_PATH} && \ $(MAKE) build diff --git a/supportlibs/libsam3 b/supportlibs/libsam3 new file mode 160000 index 000000000..8623304b6 --- /dev/null +++ b/supportlibs/libsam3 @@ -0,0 +1 @@ +Subproject commit 8623304b62294dafbe477573f321a464fef721dd From aef993de55e01cfc1ccb0d3b4f174a580aca59c9 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 27 Nov 2020 20:09:59 +0100 Subject: [PATCH 15/20] reduce log --- libretroshare/src/services/autoproxy/p3i2psam3.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp index a708517e2..949de2a00 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.cpp +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -29,7 +29,7 @@ static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; static constexpr bool kDefaultSAM3Enable = false; -RS_SET_CONTEXT_DEBUG_LEVEL(4) +RS_SET_CONTEXT_DEBUG_LEVEL(2) static void inline doSleep(std::chrono::duration> timeToSleepMS) { std::this_thread::sleep_for(timeToSleepMS); @@ -48,7 +48,7 @@ p3I2pSam3::p3I2pSam3(p3PeerMgr *peerMgr) : mSetting.enable = kDefaultSAM3Enable; mSetting.session = nullptr; - libsam3_debug = 1; + libsam3_debug = 0; } bool p3I2pSam3::isEnabled() From 54184839346f707d3cc5a353281f91c16d4aff7b Mon Sep 17 00:00:00 2001 From: sehraf Date: Sat, 28 Nov 2020 11:16:55 +0100 Subject: [PATCH 16/20] fixed wrong size check --- libretroshare/src/util/i2pcommon.cpp | 8 +++++-- libretroshare/src/util/i2pcommon.h | 33 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index 0733a807d..524219eb3 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -50,8 +50,10 @@ std::string publicKeyFromPrivate(std::string const &priv) * https://geti2p.net/spec/common-structures#keysandcert * https://geti2p.net/spec/common-structures#certificate */ - if (priv.empty() || priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys) + if (priv.length() < privKeyMinLenth_b64) { + RS_WARN("key to short!"); return std::string(); + } // creat a copy to work on, need to convert it to standard base64 auto priv_copy(priv); @@ -163,8 +165,10 @@ std::string publicKeyFromPrivate(std::string const &priv) bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &cryptoKey) { - if (key.length() < 522) // base64 (391 bytes = 384 bytes + 7 bytes = KeysAndCert + Certificate) + if (key.length() < pubKeyMinLenth_b64) { + RS_WARN("key to short!"); return false; + } // creat a copy to work on, need to convert it to standard base64 auto key_copy(key); diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h index 0a76fa080..f0da0322b 100644 --- a/libretroshare/src/util/i2pcommon.h +++ b/libretroshare/src/util/i2pcommon.h @@ -186,6 +186,39 @@ static const std::array, 12> signingKeyLengths { /*SigningKeyType::RedDSA_SHA512_Ed25519 */ std::make_pair( 32, 32), }; +/* + * Key length infos: + * + * BOB private key + * len b64: 884 + * len pln: 663 + * + * BOB public key / destination + * len b64: 516 + * len pln: 387 + * + * SAMv3 private key + * len b64: 908 + * len pln: 679 + * + * SAMv3 public key + * len b64: 516 + * len pln: 387 + * + * Example: + * in bytes, public key only + * 384 (Key) + 3 (Null certificate) = 387 bytes + * 384 (Key) + 7 (key certificate) = 391 bytes + * + * in bytes public + private key + * 384 (Key) + 3 (Null certificate) + 256 (ElGamal) + 20 (DSA_SHA1) = 663 bytes + * 384 (Key) + 7 (key certificate) + 256 (ElGamal) + 32 (EdDSA_SHA512_Ed25519) = 679 bytes + */ +constexpr size_t pubKeyMinLenth_b64 = 516; +constexpr size_t pubKeyMinLenth_bin = 387; +constexpr size_t privKeyMinLenth_b64 = 884; +constexpr size_t privKeyMinLenth_bin = 663; + /** * @brief makeOption Creates the string "lhs=rhs" used by BOB and SAM. Converts rhs * @param lhs option to set From 27fa8a35b0d1d98cccf380d67e24f4b4428cb7f6 Mon Sep 17 00:00:00 2001 From: sehraf Date: Tue, 15 Dec 2020 21:44:01 +0100 Subject: [PATCH 17/20] fix key generation and cleanup --- .../src/gui/settings/ServerPage.cpp | 128 +++--------------- 1 file changed, 21 insertions(+), 107 deletions(-) diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index ee71a9b44..8963ed28b 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -1452,8 +1452,6 @@ void ServerPage::getNewKey() { i2p::address *addr = new i2p::address(); rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::receiveKey, this, addr); - - updateStatus(); } void ServerPage::loadKey() @@ -1550,14 +1548,22 @@ void ServerPage::taskFinished(taskTicket *&ticket) RS_WARN("auto proxy task finished but not for SMA, not exptected! Also not a serious problem."); else { // update settings - mSamSettings.address = *addr; + auto copy = *addr; + RsQThreadUtils::postToObject( + [this, copy]() + { + mSamSettings.address = copy; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); + updateStatusSam(); + + }); } delete addr; addr = nullptr; ticket->data = nullptr; - updateStatusSam(); + break; } case autoProxyTask::establishConnection: @@ -1568,13 +1574,19 @@ void ServerPage::taskFinished(taskTicket *&ticket) if (ticket->types.front() != autoProxyType::I2PSAM3) RS_WARN("auto proxy task finished but not for SMA, not exptected! Also not a serious problem."); else { + bool res = ticket->result == autoProxyStatus::ok && !!secw->connection->ses; // update settings - if (secw->connection->ses) { - updateInProxyIndicatorResult(true); + if (res) { sam3CloseConnection(secw->connection); secw->connection = nullptr; // freed by above call - } else - updateInProxyIndicatorResult(false); + } + + RsQThreadUtils::postToObject( + [this, res]() + { + updateInProxyIndicatorResult(res); + + }); } if (secw->connection) @@ -1700,7 +1712,7 @@ void ServerPage::updateStatusSam() ui.pbBobGenAddr->hide(); } - saveAddresses(); + saveAddresses(); } samStatus ss; @@ -1764,105 +1776,7 @@ void ServerPage::updateStatusSam() ui.iconlabel_i2p_bob->setToolTip(s); bobSimpleText.append(s); - /* - // update BOB UI based on state - std::string errorString; - switch (bs.cs) { - case csDoConnect: - case csConnected: - case csDoDisconnect: - case csWaitForBob: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); - enableSamElements(false); - - { - QString s; - switch (bs.ct) { - case ctRunCheck: - s = tr("connectivity check"); - break; - case ctRunGetKeys: - s = tr("generating key"); - break; - case ctRunSetUp: - s = tr("starting up"); - break; - case ctRunShutDown: - s = tr("shuting down"); - default: - break; - } - bobSimpleText.append(tr("BOB is processing a request: %1").arg(s)); - } - - ui.pbBobStart->setEnabled(false); - ui.pbBobRestart->setEnabled(false); - ui.pbBobStop->setEnabled(false); - break; - case csError: - // get error msg from bob - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::getErrorInfo, &errorString); - - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_ERROR)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB is broken\n") + QString::fromStdString(errorString)); - - enableSamElements(false); - - bobSimpleText.append(tr("BOB encountered an error:\n")); - bobSimpleText.append(QString::fromStdString(errorString)); - - ui.pbBobStart->setEnabled(true); - ui.pbBobRestart->setEnabled(false); - ui.pbBobStop->setEnabled(true); - break; - case csDisconnected: - case csIdel: - switch (bs.ct) { - case ctRunSetUp: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is running")); - - enableSamElements(false); - - bobSimpleText.append(tr("BOB is working fine: tunnel established")); - - ui.pbBobStart->setEnabled(false); - ui.pbBobRestart->setEnabled(true); - ui.pbBobStop->setEnabled(true); - break; - case ctRunCheck: - case ctRunGetKeys: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); - - enableSamElements(false); - - bobSimpleText.append(tr("BOB is processing a request")); - - ui.pbBobStart->setEnabled(false); - ui.pbBobRestart->setEnabled(false); - ui.pbBobStop->setEnabled(false); - break; - case ctRunShutDown: - case ctIdle: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is not running")); - - enableSamElements(true); - - bobSimpleText.append(tr("BOB is inactive: tunnel closed")); - - ui.pbBobStart->setEnabled(true); - ui.pbBobRestart->setEnabled(false); - ui.pbBobStop->setEnabled(false); - break; - } - break; - - } - */ ui.pteBobSimple->setPlainText(bobSimpleText); From 911cbeb61a52d812b5773507ab6f2e12f62415f0 Mon Sep 17 00:00:00 2001 From: sehraf Date: Sat, 28 Nov 2020 11:32:53 +0100 Subject: [PATCH 18/20] fix key generation and cleanup --- libretroshare/src/services/autoproxy/p3i2psam3.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp index 949de2a00..c9e77273e 100644 --- a/libretroshare/src/services/autoproxy/p3i2psam3.cpp +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -506,10 +506,10 @@ bool p3I2pSam3::startSession() if(!mSetting.address.privateKey.empty()) { RS_DBG3("with destination"); - ret = sam3CreateSilentSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, mSetting.address.privateKey.c_str(), Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); + ret = sam3CreateSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, mSetting.address.privateKey.c_str(), Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); } else { RS_DBG("without destination"); - ret = sam3CreateSilentSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, SAM3_DESTINATION_TRANSIENT, Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); + ret = sam3CreateSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, SAM3_DESTINATION_TRANSIENT, Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); } } @@ -561,6 +561,7 @@ bool p3I2pSam3::startForwarding() RS_STACK_MUTEX(mLockSam3Access); + mSetting.session->silent = true; int ret = sam3StreamForward(mSetting.session, sockaddr_storage_iptostring(ps.localaddr).c_str(), sockaddr_storage_port(ps.localaddr)); if (ret < 0) { RS_DBG("forward failed, due to", mSetting.session->error); @@ -686,6 +687,7 @@ void p3I2pSam3::establishConnection(taskTicket *ticket) { auto l = this->mLockSam3Access; RS_STACK_MUTEX(l); + mSetting.session->silent = false; connection = sam3StreamConnect(this->mSetting.session, wrapper->address.publicKey.c_str()); } From feaea75847793af13c0bcc4e0c609a1321679e44 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 18 Dec 2020 18:53:28 +0100 Subject: [PATCH 19/20] fixup rebase --- retroshare-gui/src/gui/settings/ServerPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index 8963ed28b..5755b4015 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -87,7 +87,7 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); -#ifndef RS_USE_I2P_BOB +#ifndef RS_USE_I2P_SAM3 ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P); // warning: the order of operation here is very important. #endif From b4be02c57b9245e37f3ab666f1d0243da6915be9 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 9 Apr 2021 16:15:40 +0200 Subject: [PATCH 20/20] rebase on 0.6.6 --- retroshare-gui/src/gui/settings/ServerPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index 5755b4015..cdbe5c3e3 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -113,7 +113,7 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) ui.hiddenpage_outHeader->setText(tr("Tor has been automatically configured by Retroshare. You shouldn't need to change anything here.")) ; ui.hiddenpage_inHeader->setText(tr("Tor has been automatically configured by Retroshare. You shouldn't need to change anything here.")) ; - ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important. + ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P); // warning: the order of operation here is very important. } } else