From 433bc3ceea3157216c3044438bd8cb43d10120f4 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 02:23:48 -0400 Subject: [PATCH 01/10] * NEW [scram] NEW SCRAM library is imported. Signed-off-by: wanghaemq --- src/supplemental/CMakeLists.txt | 1 + src/supplemental/scram/CMakeLists.txt | 11 + src/supplemental/scram/scram.c | 687 ++++++++++++++++++++++++++ src/supplemental/scram/scram.h | 28 ++ src/supplemental/scram/scram_test.c | 239 +++++++++ 5 files changed, 966 insertions(+) create mode 100644 src/supplemental/scram/CMakeLists.txt create mode 100644 src/supplemental/scram/scram.c create mode 100644 src/supplemental/scram/scram.h create mode 100644 src/supplemental/scram/scram_test.c diff --git a/src/supplemental/CMakeLists.txt b/src/supplemental/CMakeLists.txt index e8749cb0..2ccc6089 100644 --- a/src/supplemental/CMakeLists.txt +++ b/src/supplemental/CMakeLists.txt @@ -19,3 +19,4 @@ add_subdirectory(mqtt) add_subdirectory(sqlite) add_subdirectory(quic) add_subdirectory(nanolib) +add_subdirectory(scram) diff --git a/src/supplemental/scram/CMakeLists.txt b/src/supplemental/scram/CMakeLists.txt new file mode 100644 index 00000000..fc5ccc16 --- /dev/null +++ b/src/supplemental/scram/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# This software is supplied under the terms of the MIT License, a +# copy of which should be located in the distribution where this +# file was obtained (LICENSE.txt). A copy of the license may also be +# found online at https://opensource.org/licenses/MIT. +# + +if (NNG_ENABLE_SCRAM) + nng_sources(scram.c scram.h) + nng_test(scram_test) +endif () diff --git a/src/supplemental/scram/scram.c b/src/supplemental/scram/scram.c new file mode 100644 index 00000000..46e7149f --- /dev/null +++ b/src/supplemental/scram/scram.c @@ -0,0 +1,687 @@ +// +// Copyright 2024 NanoMQ Team, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include +#include + +#include +#include +#include + +#include "scram.h" + +#include "nng/supplemental/nanolib/base64.h" +#include "nng/supplemental/nanolib/log.h" + +#define SCRAM_SALT_SZ 64 + +/* TODO Is salt a global static value? +gen_salt() -> + <> = crypto:strong_rand_bytes(16), + iolist_to_binary(io_lib:format("~32.16.0b", [X])). +*/ +static int +gen_salt() +{ + return (int)nng_random(); +} + +static char * +gs_header() +{ + return (char *)"n,,"; +} + +static int +nonce() +{ + return (int)nng_random(); +} + +static int +salt_password(char *pwd, int pwdsz, char *salt, int saltsz, int iteration_cnt, const EVP_MD *digest, int keysz, char *result) +{ + return PKCS5_PBKDF2_HMAC(pwd, pwdsz, (const unsigned char *)salt, saltsz, iteration_cnt, digest, keysz, (unsigned char *)result); +} + +/* +client_key(Alg, SaltedPassword) -> + hmac(Alg, SaltedPassword, <<"Client Key">>). +server_key(Alg, SaltedPassword) -> + hmac(Alg, SaltedPassword, <<"Server Key">>). +stored_key(Alg, ClientKey) -> + crypto:hash(Alg, ClientKey). +*/ +static char * +client_key(const EVP_MD *digest, char *salt_pwd, int sz) +{ + char *key = salt_pwd; + char *data = "Client Key"; + unsigned char *md = HMAC(digest, key, sz, (const unsigned char *)data, strlen(data), NULL, NULL); + if (md != NULL) { + char *result = nng_alloc(sizeof(char) * sz); + memcpy(result, md, sz); + return result; + } + return NULL; +} + +static char * +server_key(const EVP_MD *digest, char *salt_pwd, int sz) +{ + char *key = salt_pwd; + char *data = "Server Key"; + unsigned char *md = HMAC(digest, key, sz, (const unsigned char *)data, strlen(data), NULL, NULL); + if (md != NULL) { + char *result = nng_alloc(sizeof(char) * sz); + memcpy(result, md, sz); + return result; + } + return NULL; +} + +static char * +hash(const EVP_MD *digest, char *data, int sz) +{ + unsigned char *out_hash = nng_alloc(sizeof(char) *EVP_MAX_MD_SIZE); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (mdctx == NULL) { + log_error("Failed to create EVP_MD_CTX\n"); + return NULL; + } + + if (1 != EVP_DigestInit_ex(mdctx, digest, NULL)) { + log_error("Failed to initialize digest\n"); + EVP_MD_CTX_free(mdctx); + return NULL; + } + + if (1 != EVP_DigestUpdate(mdctx, data, sz)) { + log_error("Failed to update digest\n"); + EVP_MD_CTX_free(mdctx); + return NULL; + } + + unsigned int out_len; + if (1 != EVP_DigestFinal_ex(mdctx, out_hash, &out_len)) { + EVP_MD_CTX_free(mdctx); + return NULL; + } + + EVP_MD_CTX_free(mdctx); + return (char *)out_hash; +} + +static char * +stored_key(const EVP_MD *digest, char *client_key, int sz) +{ + return hash(digest, client_key, sz); +} + +static void +xor(char *in1, char *in2, char *out, int len) +{ + for (int i=0; idigest, key, keysz, (const unsigned char *)data, strlen(data), NULL, NULL); + if (md != NULL) { + char *result = nng_alloc(sizeof(char) * keysz); + memcpy(result, md, keysz); + return result; + } + return NULL; +} + +void +scram_ctx_free(void *arg) +{ + struct scram_ctx *ctx = arg; + if (!ctx) + return; + if (ctx->pwd) nng_free(ctx->pwd, 0); + if (ctx->salt) nng_free(ctx->salt, 0); + if (ctx->salt_pwd) nng_free(ctx->salt_pwd, 0); + if (ctx->client_key) nng_free(ctx->client_key, 0); + if (ctx->server_key) nng_free(ctx->server_key, 0); + if (ctx->stored_key) nng_free(ctx->stored_key, 0); + if (ctx->cached_nonce) nng_free(ctx->cached_nonce, 0); + + if (ctx->client_final_msg_without_proof) nng_free(ctx->client_final_msg_without_proof, 0); + if (ctx->server_first_msg) nng_free(ctx->server_first_msg, 0); + if (ctx->client_first_msg) nng_free(ctx->client_first_msg, 0); + nng_free(ctx, 0); +} + +static int +scram_ctx_update(void *arg, char *salt) +{ + struct scram_ctx *ctx = arg; + int keysz = ctx->digestsz; + + ctx->salt = salt; + if (ctx->salt == NULL) { + return -1; + } + + char *salt_pwd = nng_alloc(sizeof(char) * ctx->digestsz); + int rv = salt_password(ctx->pwd, ctx->pwdsz, ctx->salt, strlen(ctx->salt), + ctx->iteration_cnt, ctx->digest, ctx->digestsz, salt_pwd); + if (rv != 1) { + log_error("salt password failed %d???\n", rv); + nng_free(salt_pwd, 0); + nng_free(ctx->salt, 0); + return -2; + } + ctx->salt_pwd = salt_pwd; + + ctx->client_key = client_key(ctx->digest, salt_pwd, keysz); + ctx->server_key = server_key(ctx->digest, salt_pwd, keysz); + ctx->stored_key = stored_key(ctx->digest, ctx->client_key, keysz); + + return 0; +} + +void * +scram_ctx_create(char *pwd, int pwdsz, int iteration_cnt, enum SCRAM_digest dig, int salt) +{ + int rv; + int keysz; + const EVP_MD *digest; + switch (dig) { + case SCRAM_SHA1: + digest = EVP_sha1(); + keysz = 20; // 160 bits + break; + case SCRAM_SHA256: + digest = EVP_sha256(); + keysz = 32; // 256 bits + break; + default: + log_error("wrong SCRAM_TYPE\n"); + return NULL; + } + struct scram_ctx *ctx = nng_alloc(sizeof(struct scram_ctx)); + if (ctx == NULL) { + log_error("no memory\n"); + return NULL; + } + memset(ctx, 0, sizeof(*ctx)); + + ctx->pwd = strndup(pwd, pwdsz); + ctx->pwdsz = pwdsz; + ctx->digest = digest; + ctx->digestsz = keysz; + ctx->iteration_cnt = iteration_cnt; + + if (salt == 0) + return (void *)ctx; + + salt = gen_salt(); + char *saltstr = nng_alloc(sizeof(char) * SCRAM_SALT_SZ); + if (saltstr == NULL) { + nng_free(ctx, 0); + return NULL; + } + sprintf(saltstr, "%d", salt); + if (0 != (rv = scram_ctx_update(ctx, saltstr))) { + log_error("error in updating ctx %d", rv); + nng_free(ctx, 0); + return NULL; + } + + /* + char *salt_pwd = nng_alloc(sizeof(char) * keysz); + rv = salt_password(pwd, pwdsz, ctx->salt, strlen(ctx->salt), + iteration_cnt, digest, keysz, salt_pwd); + if (rv != 1) { + log_error("salt password failed %d???\n", rv); + nng_free(salt_pwd, 0); + nng_free(ctx->salt, 0); + nng_free(ctx, 0); + return NULL; + } + ctx->salt_pwd = salt_pwd; + + ctx->client_key = client_key(digest, salt_pwd, keysz); + ctx->server_key = server_key(digest, salt_pwd, keysz); + ctx->stored_key = stored_key(digest, ctx->client_key, keysz); + */ + + /* debug + for (int i=0; i>> SALT\n"); + for (int i=0; iclient_key[i] & 0xff); + printf(">>> CLIKEY\n"); + for (int i=0; iserver_key[i] & 0xff); + printf(">>> SERKEY\n"); + for (int i=0; istored_key[i] & 0xff); + printf(">>> STOREDKEY\n"); + */ + + return (void *)ctx; +} + +/* +%% client-first-message-bare = [reserved-mext ","] userame "," nonce ["," extensions] +client_first_message_bare(Username) -> + iolist_to_binary(["n=", Username, ",r=", nonce()]). +*/ +char * +scram_client_first_msg(void *arg, const char *username) +{ + struct scram_ctx *ctx = arg; + char client_first_msg_bare[strlen(username) + 32]; + sprintf(client_first_msg_bare, "n=%s,r=%d", username, nonce()); + + int sz = strlen(username) + SCRAM_SALT_SZ + 32; // gs_header + username + nonce + char *buf = nng_alloc(sizeof(char) * sz); + + sprintf(buf, "%s%s", gs_header(), client_first_msg_bare); + ctx->client_first_msg_bare = buf + strlen(gs_header()); + ctx->client_first_msg = buf; + return buf; +} + +/* +client_final_message_without_proof(Nonce) -> + iolist_to_binary(["c=", base64:encode(gs2_header()), ",r=", Nonce]). + +client_final_message(Nonce, Proof) -> + iolist_to_binary([client_final_message_without_proof(Nonce), ",p=", base64:encode(Proof)]). +*/ +static char * +scram_client_final_msg(char *nonce, const char *proof, int client_proofsz) +{ + char *gh = gs_header(); + size_t ghb64sz = BASE64_ENCODE_OUT_SIZE(strlen(gh)) + 1; + char ghb64[ghb64sz]; + size_t proofb64sz = BASE64_ENCODE_OUT_SIZE(client_proofsz) + 1; + char proofb64[proofb64sz]; + if (0 == base64_encode((const unsigned char *)gh, strlen(gh), ghb64)) { + return NULL; + } + if (0 == base64_encode((const unsigned char *)proof, client_proofsz, proofb64)) { + return NULL; + } + char *buf = malloc(sizeof(char) * (ghb64sz + proofb64sz + 32)); + + sprintf(buf, "c=%s,r=%s,p=%s", ghb64, nonce, proofb64); + return buf; +} + +/* +server_first_message(Nonce, Salt, IterationCount) -> + iolist_to_binary(["r=", Nonce, ",s=", base64:encode(Salt), ",i=", integer_to_list(IterationCount)]). +*/ +static char * +scram_server_first_msg(char *nonce, const char *salt, int iteration_cnt) +{ + size_t saltb64sz = BASE64_ENCODE_OUT_SIZE(strlen(salt)) + 1; + char saltb64[saltb64sz]; + if (0 == base64_encode((const unsigned char *)salt, strlen(salt), saltb64)) { + return NULL; + } + char *buf = nng_alloc(sizeof(char) * (saltb64sz + 64)); + sprintf(buf, "r=%s,s=%s,i=%d", nonce, saltb64, iteration_cnt); + return buf; +} + +/* +server_final_message(verifier, ServerSignature) -> + iolist_to_binary(["v=", base64:encode(ServerSignature)]); +server_final_message(error, Error) -> + iolist_to_binary(["e=", Error]). +*/ +static char * +scram_server_final_msg(const char *server_sig, int sz, int error) +{ + char *buf; + if (error != 0) { + buf = nng_alloc(sizeof(char) * 32); + sprintf(buf, "e=%d", error); + return buf; + } + size_t ssb64sz = BASE64_ENCODE_OUT_SIZE(sz) + 1; + char ssb64[ssb64sz]; + if (0 == base64_encode((const unsigned char *)server_sig, sz, ssb64)) { + return NULL; + } + buf = nng_alloc(sizeof(char) * (ssb64sz + 32)); + sprintf(buf, "v=%s", ssb64); + return buf; +} + +/* +static int +get_comma_value_len(char *payload, char *payload_end) +{ + int len = 0; + char *it = payload; + while (it != (payload_end + 1)) { + if (*it == ',') + break; + it ++; + len ++; + } + return len; +} + +static char * +get_next_comma_value(char *payload, char *payload_end) +{ + char *it = payload; + while (it != (payload_end + 1)) { + if (*it == ',') + break; + it++; + } + if (it == (payload_end + 1)) + return NULL; + return it + 1; +} +*/ + +static char * +get_comma_value(char *payload, char *payload_end, char **next_start, int peekn) +{ + int len = 0; + char *it = payload; + while (it != (payload_end + 1)) { + if (*it == ',') + break; + it++; + len++; + } + *next_start = (it + 1); + if (it == (payload_end + 1)) { + *next_start = it; + if (len > 0) + return strndup(payload + peekn, len - peekn); + return NULL; + } + if (len > 0) + return strndup(payload + peekn, len - peekn); + return NULL; +} + +// %% = gs2-cbind-flag "," [authzid] "," [reserved-mext ","] userame "," nonce ["," extensions] +char * +scram_handle_client_first_msg(void *arg, const char *msg, int len) +{ + struct scram_ctx *ctx = arg; + char *it = (char *)msg; + char *itend = it + len - 1; + char *itnext; + char *gs2_cbind_flag = get_comma_value(it, itend, &itnext, 0); + it = itnext; + char *authzid = get_comma_value(it, itend, &itnext, 0); + it = itnext; + + /* + peek_client_first_message_bare(Bin) -> + [_, One] = binary:split(Bin, <<",">>), + [_, Two] = binary:split(One, <<",">>), + Two. + */ + ctx->client_first_msg = strndup(msg, len); + ctx->client_first_msg_bare = ctx->client_first_msg + (it - msg); + + //char *reserved_mext = get_next_comma_value(it, itend); + //int reserved_mextsz = get_comma_value_len(it, itend); + //it += (reserved_mextsz + 1); + char *username = get_comma_value(it, itend, &itnext, 2); + it = itnext; + char *cnonce = get_comma_value(it, itend, &itnext, 2); + it = itnext; + char *extensions = get_comma_value(it, itend, &itnext, 0); + // parse done + int snonce = nonce(); + char csnonce[64]; + sprintf(csnonce, "%s%d", cnonce, snonce); + char *salt = ctx->salt; + int iteration_cnt = ctx->iteration_cnt; + char *server_first_msg = scram_server_first_msg(csnonce, salt, iteration_cnt); + ctx->server_first_msg = strdup(server_first_msg); + ctx->cached_nonce = strdup(csnonce); + + nng_free(gs2_cbind_flag, 0); + nng_free(authzid, 0); + nng_free(username, 0); + nng_free(cnonce, 0); + nng_free(extensions, 0); + return server_first_msg; +} + +/* +peek_client_final_message_without_proof(Bin) -> + [ClientFinalMessageWithoutProof | _] = binary:split(Bin, <<",p=">>, [trim_all]), + ClientFinalMessageWithoutProof. +*/ +static char * +peek_client_final_msg_without_proof(const char *msg) +{ + char *m = strdup(msg); + char *end = strstr(m, ",p="); + *end = '\0'; + return m; +} + +// %% = channel-binding "," nonce ["," extensions] "," proof +char * +scram_handle_client_final_msg(void *arg, const char *msg, int len) +{ + struct scram_ctx *ctx = arg; + char *result = NULL; + char *it = (char *)msg; + char *itend = it + len - 1; + char *itnext; + char *gs2_cbind_flag = get_comma_value(it, itend, &itnext, 2); + it = itnext; + char *csnonce = get_comma_value(it, itend, &itnext, 2); + it = itnext; + char *proof = get_comma_value(it, itend, &itnext, 2); + it = itnext; + // parse done + //AuthMessage = ([ ClientFirstMessageBare,ServerFirstMessage,ClientFinalMessageWithoutProof]), + char *client_final_msg_without_proof = peek_client_final_msg_without_proof(msg); + char authmsg[512]; + sprintf(authmsg, "%s,%s,%s", + ctx->client_first_msg_bare, ctx->server_first_msg, client_final_msg_without_proof); + log_trace("handle client final authmsg: %s\n", authmsg); + // ClientSignature = hmac(Algorithm, StoredKey, AuthMessage), + char *client_sig = scram_hmac(ctx, ctx->stored_key, ctx->digestsz, authmsg); + // ClientKey = crypto:exor(ClientProof, ClientSignature) + int proofsz = ctx->digestsz; + char client_key[proofsz]; + char client_proof[proofsz + 1]; + if (0 == base64_decode(proof, strlen(proof), (unsigned char *)client_proof)) { + return NULL; + } + xor(client_proof, client_sig, client_key, proofsz); + /* + case Nonce =:= CachedNonce andalso crypto:hash(Algorithm, ClientKey) =:= StoredKey of + true -> + ServerSignature = hmac(Algorithm, ServerKey, AuthMessage), + ServerFinalMessage = server_final_message(verifier, ServerSignature), + {ok, ServerFinalMessage}; + false -> + {error, 'other-error'} + end; + */ + char *hash_client_key = hash(ctx->digest, client_key, ctx->digestsz); + if (ctx->cached_nonce && + 0 == strcmp(csnonce, ctx->cached_nonce) && + 0 == strncmp(hash_client_key, ctx->stored_key, ctx->digestsz)) { + //0 == strcmp(csnonce, ctx->cached_nonce)) { + char *server_sig = scram_hmac(ctx, ctx->server_key, ctx->digestsz, authmsg); + + /* + printf(">>> server: SERVER_SIG "); + for (int i=0; idigestsz; ++i) + printf("%d,", server_sig[i] & 0xff); + printf("<<<\n"); + printf("server: server_key %.*s\n", ctx->digestsz, ctx->server_key); + */ + char *server_final_msg = scram_server_final_msg(server_sig, ctx->digestsz, 0); + result = server_final_msg; + nng_free(server_sig, 0); + } + nng_free(gs2_cbind_flag, 0); + nng_free(hash_client_key, 0); + nng_free(client_sig, 0); + nng_free(csnonce, 0); + nng_free(proof, 0); + nng_free(client_final_msg_without_proof, 0); + return result; +} + +/* +client_final_message_without_proof(Nonce) -> + iolist_to_binary(["c=", base64:encode(gs2_header()), ",r=", Nonce]). +*/ +// %% = [reserved-mext ","] nonce "," salt "," iteration-count ["," extensions] +char * +scram_handle_server_first_msg(void *arg, const char *msg, int len) +{ + struct scram_ctx *ctx = arg; + char *it = (char *)msg; + char *itend = it + len - 1; + char *itnext; + char *nonce = get_comma_value(it, itend, &itnext, 2); + it = itnext; + char *saltb64 = get_comma_value(it, itend, &itnext, 2); + it = itnext; + char *iteration_cnt = get_comma_value(it, itend, &itnext, 2); + // parse done + ctx->server_first_msg = strndup(msg, len); + char *salt = nng_alloc(sizeof(char) * SCRAM_SALT_SZ); + memset(salt, 0, SCRAM_SALT_SZ); + if (0 == base64_decode(saltb64, strlen(saltb64), (unsigned char *)salt)) { + return NULL; + } + + scram_ctx_update(ctx, salt); + //ClientFinalMessageWithoutProof = client_final_message_without_proof(Nonce), + char *gh = gs_header(); + size_t ghb64sz = BASE64_ENCODE_OUT_SIZE(strlen(gh)) + 1; + char ghb64[ghb64sz]; + if (0 == base64_encode((const unsigned char *)gh, strlen(gh), ghb64)) { + return NULL; + } + char client_final_msg_without_proof[32 + SCRAM_SALT_SZ]; + sprintf(client_final_msg_without_proof, "c=%s,r=%s", ghb64, nonce); + ctx->client_final_msg_without_proof = strdup(client_final_msg_without_proof); + // authmsg=[ClientFirstMessageBare,ServerFirstMessage,ClientFinalMessageWithoutProof] + char authmsg[512]; + sprintf(authmsg, "%s,%s,%s", + ctx->client_first_msg_bare, + ctx->server_first_msg, + client_final_msg_without_proof); + //printf("handle server first authmsg: %s\n", authmsg); + /* + SaltedPassword = salted_password(Algorithm, Password, Salt, IterationCount), + ClientKey = client_key(Algorithm, SaltedPassword), + StoredKey = stored_key(Algorithm, ClientKey), + ClientSignature = hmac(Algorithm, StoredKey, AuthMessage), + ClientProof = crypto:exor(ClientKey, ClientSignature), + */ + char *client_sig = scram_hmac(ctx, ctx->stored_key, ctx->digestsz, authmsg); + int client_sig_len = ctx->digestsz; + char client_proof[client_sig_len]; + xor(ctx->client_key, client_sig, client_proof, client_sig_len); + + char *client_final_msg = scram_client_final_msg(nonce, client_proof, client_sig_len); + + nng_free(client_sig, client_sig_len); + nng_free(nonce, 0); + nng_free(saltb64, 0); + nng_free(iteration_cnt, 0); + return client_final_msg; +} + +// %% = (server-error / verifier) ["," extensions] +char * +scram_handle_server_final_msg(void *arg, const char *msg, int len) +{ + char *result = NULL; + struct scram_ctx *ctx = arg; + char *it = (char *)msg; + char *itend = it + len - 1; + char *itnext; + char *verifier = get_comma_value(it, itend, &itnext, 2); + it = itnext; + //char *extensions = get_next_comma_value(it, itend); + //int extensionssz = get_comma_value_len(it); + // parse done + /* + ClientFinalMessageWithoutProof = client_final_message_without_proof(Nonce), + authmsg=[ClientFirstMessageBare,ServerFirstMessage,ClientFinalMessageWithoutProof] + */ + char authmsg[512]; + sprintf(authmsg, "%s,%s,%s", + ctx->client_first_msg_bare, + ctx->server_first_msg, + ctx->client_final_msg_without_proof); + log_trace("handle server final authmsg: %s\n", authmsg); + /* + case Verifier =:= hmac(Algorithm, ServerKey, AuthMessage) of + true -> + ok; + false -> + {error, 'other-error'} + end; + */ + char *server_sig = scram_hmac(ctx, ctx->server_key, ctx->digestsz, authmsg); + log_trace("client: server_key %.*s\n", ctx->digestsz, ctx->server_key); + size_t ssb64sz = BASE64_ENCODE_OUT_SIZE(ctx->digestsz) + 1; + char ssb64[ssb64sz]; + if (0 == base64_encode((const unsigned char *)server_sig, ctx->digestsz, ssb64)) { + return NULL; + } + + if (0 == strcmp(verifier, ssb64)) { + result = arg; // Successfully + } + nng_free(server_sig, 0); + nng_free(verifier, 0); + return result; +} + diff --git a/src/supplemental/scram/scram.h b/src/supplemental/scram/scram.h new file mode 100644 index 00000000..e7d53ec3 --- /dev/null +++ b/src/supplemental/scram/scram.h @@ -0,0 +1,28 @@ +// +// Copyright 2024 NanoMQ Team, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef NNG_SUPP_SCRAM_H +#define NNG_SUPP_SCRAM_H + +enum SCRAM_digest { + SCRAM_SHA1, + SCRAM_SHA256 +}; + +void *scram_ctx_create(char *pwd, int pwdsz, int it_cnt, enum SCRAM_digest digest, int salt); +void scram_ctx_free(void *ctx); + +char *scram_client_first_msg(void *arg, const char *username); + +char *scram_handle_client_first_msg(void *arg, const char *msg, int len); +char *scram_handle_server_first_msg(void *arg, const char *msg, int len); +char *scram_handle_client_final_msg(void *arg, const char *msg, int len); +char *scram_handle_server_final_msg(void *arg, const char *msg, int len); + +#endif diff --git a/src/supplemental/scram/scram_test.c b/src/supplemental/scram/scram_test.c new file mode 100644 index 00000000..55972029 --- /dev/null +++ b/src/supplemental/scram/scram_test.c @@ -0,0 +1,239 @@ +// +// Copyright 2024 NanoMQ Team, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + + +#include +#include + +#include "scram.h" + +#include + +void +test_client_first_msg(void) +{ + char *username = "admin"; + char *pwd = "public"; + + void *ctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256, 0); + NUTS_ASSERT(NULL != ctx); + char *client_first_msg = scram_client_first_msg(ctx, username); + NUTS_ASSERT(NULL != client_first_msg); + + // We don't care about the random + char expect_first_msg[256]; + sprintf(expect_first_msg, "n,,n=%s,r=", username); + NUTS_ASSERT(0 == strncmp(client_first_msg, expect_first_msg, strlen(expect_first_msg))); + + printf("first msg:%s\n", client_first_msg); + + scram_ctx_free(ctx); +} + +void +test_handle_client_first_msg(void) +{ + char *username = "admin"; + char *pwd = "public"; + int salt = nng_random(); + char *client_first_msg = "n,,n=admin,r=588996903"; + + void *ctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256, salt); + NUTS_ASSERT(NULL != ctx); + + char *server_first_msg = + scram_handle_client_first_msg(ctx, client_first_msg, strlen(client_first_msg)); + NUTS_ASSERT(NULL != server_first_msg); + + printf("server first msg: %s\n", server_first_msg); + + nng_free(server_first_msg, 0); + scram_ctx_free(ctx); + (void)username; +} + +void +test_handle_server_first_msg(void) +{ + char *username = "admin"; + char *pwd = "public"; + char *server_first_msg = "r=5889969031670468145,s=MTcxMDYxMjE0Mw==,i=4096"; + + void *ctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256, 0); + NUTS_ASSERT(NULL != ctx); + + char *client_final_msg = + scram_handle_server_first_msg(ctx, server_first_msg, strlen(server_first_msg)); + NUTS_ASSERT(NULL != client_final_msg); + + printf("client final msg: %s\n", client_final_msg); + + nng_free(client_final_msg, 0); + scram_ctx_free(ctx); + (void)username; +} + +void +test_handle_client_final_msg(void) +{ + char *username = "admin"; + char *pwd = "public"; + int salt = nng_random(); + char *client_final_msg = "c=biws,r=5889969031670468145,p=DtmY/yJcVDnfUT4hDRF+pvsG6ec8dctrlNe1XO7er2c="; + + void *ctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256, salt); + NUTS_ASSERT(NULL != ctx); + + char *server_final_msg = + scram_handle_client_final_msg(ctx, client_final_msg, strlen(client_final_msg)); + // NUTS_ASSERT(NULL != server_final_msg); + NUTS_ASSERT(NULL == server_final_msg); // Due to no ctx + + // printf("server final msg: %s\n", server_final_msg); + + nng_free(server_final_msg, 0); + scram_ctx_free(ctx); + (void)username; +} + +/* +void +test_handle_server_final_msg(void) +{ + char *username = "admin"; + char *pwd = "public"; + char *server_final_msg = "c=biws,r=5889969031670468145,p=3f+0DVmROdCQE/nAMo4wIKobP6TolZOEldP7s2wCykM="; + + void *ctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256); + NUTS_ASSERT(NULL != ctx); + + char *server_final_msg = + scram_handle_client_final_msg(ctx, client_final_msg, strlen(client_final_msg)); + //NUTS_ASSERT(NULL != server_final_msg); + NUTS_ASSERT(NULL == server_final_msg); // Due to no ctx + + //printf("server final msg: %s\n", server_final_msg); + + nng_free(server_final_msg, 0); + scram_ctx_free(ctx); + (void)username; +} +*/ + +void +test_full_auth(void) +{ + char *username = "admin"; + char *pwd = "public"; + int salt = nng_random(); + + void *cctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256, 0); + NUTS_ASSERT(NULL != cctx); + + void *sctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256, salt); + NUTS_ASSERT(NULL != sctx); + + // client generate first msg + char *client_first_msg = scram_client_first_msg(cctx, username); + NUTS_ASSERT(NULL != client_first_msg); + printf("client first msg: %s\n", client_first_msg); + + // server recv client_first_msg and return server_first_msg + char *server_first_msg = + scram_handle_client_first_msg(sctx, client_first_msg, strlen(client_first_msg)); + NUTS_ASSERT(NULL != server_first_msg); + printf("server first msg: %s\n", server_first_msg); + + // client recv server_first_msg and return client_final_msg + char *client_final_msg = + scram_handle_server_first_msg(cctx, server_first_msg, strlen(server_first_msg)); + NUTS_ASSERT(NULL != client_final_msg); + printf("client final msg: %s\n", client_final_msg); + + // server recv client_final_msg and return server_final_msg + char *server_final_msg = + scram_handle_client_final_msg(sctx, client_final_msg, strlen(client_final_msg)); + NUTS_ASSERT(NULL != server_final_msg); + printf("server final msg: %s\n", server_final_msg); + + // client recv server_final_msg and return result + char *result = + scram_handle_server_final_msg(cctx, server_final_msg, strlen(server_final_msg)); + NUTS_ASSERT(NULL != result); + + nng_free(server_first_msg, 0); + nng_free(client_final_msg, 0); + nng_free(server_final_msg, 0); + + scram_ctx_free(cctx); + scram_ctx_free(sctx); +} + +void +test_full_auth_unmatch(void) +{ + char *username = "admin"; + char *pwd = "public"; + char *pwd2 = "closed"; + int salt = nng_random(); + + void *cctx = scram_ctx_create(pwd, strlen(pwd), 4096, SCRAM_SHA256, 0); + NUTS_ASSERT(NULL != cctx); + + void *sctx = scram_ctx_create(pwd2, strlen(pwd), 4096, SCRAM_SHA256, salt); + NUTS_ASSERT(NULL != sctx); + + // client generate first msg + char *client_first_msg = scram_client_first_msg(cctx, username); + NUTS_ASSERT(NULL != client_first_msg); + printf("client first msg: %s\n", client_first_msg); + + // server recv client_first_msg and return server_first_msg + char *server_first_msg = + scram_handle_client_first_msg(sctx, client_first_msg, strlen(client_first_msg)); + NUTS_ASSERT(NULL != server_first_msg); + printf("server first msg: %s\n", server_first_msg); + + // client recv server_first_msg and return client_final_msg + char *client_final_msg = + scram_handle_server_first_msg(cctx, server_first_msg, strlen(server_first_msg)); + NUTS_ASSERT(NULL != client_final_msg); + printf("client final msg: %s\n", client_final_msg); + + // server recv client_final_msg and return server_final_msg + char *server_final_msg = + scram_handle_client_final_msg(sctx, client_final_msg, strlen(client_final_msg)); + NUTS_ASSERT(NULL == server_final_msg); + printf("server final msg: %s\n", server_final_msg); + + /* + // client recv server_final_msg and return result + char *result = + scram_handle_server_final_msg(cctx, server_final_msg, strlen(server_final_msg)); + NUTS_ASSERT(NULL == result); + */ + + nng_free(server_first_msg, 0); + nng_free(client_final_msg, 0); + nng_free(server_final_msg, 0); + + scram_ctx_free(cctx); + scram_ctx_free(sctx); +} + +TEST_LIST = { + { "client first msg", test_client_first_msg }, + { "handle client first msg", test_handle_client_first_msg }, + { "handle server first msg", test_handle_server_first_msg }, + { "handle client final msg", test_handle_client_final_msg }, + //{ "handle server final msg", test_handle_server_final_msg }, + { "full enhanced auth", test_full_auth }, + { "full enhanced auth with unmatch password", test_full_auth_unmatch }, + { NULL, NULL }, +}; From b000ecdfcba55d8300ed1661adc7fd63e272ffd9 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 03:22:27 -0400 Subject: [PATCH 02/10] * NEW [scram] base64 library is imported. Signed-off-by: wanghaemq --- include/nng/supplemental/nanolib/base64.h | 21 +++ src/supplemental/nanolib/base64.c | 163 ++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 include/nng/supplemental/nanolib/base64.h create mode 100644 src/supplemental/nanolib/base64.c diff --git a/include/nng/supplemental/nanolib/base64.h b/include/nng/supplemental/nanolib/base64.h new file mode 100644 index 00000000..81564638 --- /dev/null +++ b/include/nng/supplemental/nanolib/base64.h @@ -0,0 +1,21 @@ +#ifndef BASE64_H +#define BASE64_H +#include "nng/nng.h" + +#define BASE64_ENCODE_OUT_SIZE(s) ((unsigned int)((((s) + 2) / 3) * 4 + 1)) +#define BASE64_DECODE_OUT_SIZE(s) ((unsigned int)(((s) / 4) * 3)) + +/* + * out is null-terminated encode string. + * return values is out length, exclusive terminating `\0' + */ +NNG_DECL unsigned int +base64_encode(const unsigned char *in, unsigned int inlen, char *out); + +/* + * return values is out length + */ +NNG_DECL unsigned int +base64_decode(const char *in, unsigned int inlen, unsigned char *out); + +#endif /* BASE64_H */ \ No newline at end of file diff --git a/src/supplemental/nanolib/base64.c b/src/supplemental/nanolib/base64.c new file mode 100644 index 00000000..3b4f9a09 --- /dev/null +++ b/src/supplemental/nanolib/base64.c @@ -0,0 +1,163 @@ + +#include "nng/supplemental/nanolib/base64.h" + +#define BASE64_PAD '=' +#define BASE64DE_FIRST '+' +#define BASE64DE_LAST 'z' + +/* BASE 64 encode table */ +static const char base64en[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', +}; + +/* ASCII order for BASE 64 decode, 255 in unused character */ +static const unsigned char base64de[] = { + /* nul, soh, stx, etx, eot, enq, ack, bel, */ + 255, 255, 255, 255, 255, 255, 255, 255, + + /* bs, ht, nl, vt, np, cr, so, si, */ + 255, 255, 255, 255, 255, 255, 255, 255, + + /* dle, dc1, dc2, dc3, dc4, nak, syn, etb, */ + 255, 255, 255, 255, 255, 255, 255, 255, + + /* can, em, sub, esc, fs, gs, rs, us, */ + 255, 255, 255, 255, 255, 255, 255, 255, + + /* sp, '!', '"', '#', '$', '%', '&', ''', */ + 255, 255, 255, 255, 255, 255, 255, 255, + + /* '(', ')', '*', '+', ',', '-', '.', '/', */ + 255, 255, 255, 62, 255, 255, 255, 63, + + /* '0', '1', '2', '3', '4', '5', '6', '7', */ + 52, 53, 54, 55, 56, 57, 58, 59, + + /* '8', '9', ':', ';', '<', '=', '>', '?', */ + 60, 61, 255, 255, 255, 255, 255, 255, + + /* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', */ + 255, 0, 1, 2, 3, 4, 5, 6, + + /* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', */ + 7, 8, 9, 10, 11, 12, 13, 14, + + /* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', */ + 15, 16, 17, 18, 19, 20, 21, 22, + + /* 'X', 'Y', 'Z', '[', '\', ']', '^', '_', */ + 23, 24, 25, 255, 255, 255, 255, 255, + + /* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', */ + 255, 26, 27, 28, 29, 30, 31, 32, + + /* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', */ + 33, 34, 35, 36, 37, 38, 39, 40, + + /* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', */ + 41, 42, 43, 44, 45, 46, 47, 48, + + /* 'x', 'y', 'z', '{', '|', '}', '~', del, */ + 49, 50, 51, 255, 255, 255, 255, 255 +}; + +unsigned int +base64_encode(const unsigned char *in, unsigned int inlen, char *out) +{ + int s; + unsigned int i; + unsigned int j; + unsigned char c; + unsigned char l; + + s = 0; + l = 0; + for (i = j = 0; i < inlen; i++) { + c = in[i]; + + switch (s) { + case 0: + s = 1; + out[j++] = base64en[(c >> 2) & 0x3F]; + break; + case 1: + s = 2; + out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xF)]; + break; + case 2: + s = 0; + out[j++] = base64en[((l & 0xF) << 2) | ((c >> 6) & 0x3)]; + out[j++] = base64en[c & 0x3F]; + break; + } + l = c; + } + + switch (s) { + case 1: + out[j++] = base64en[(l & 0x3) << 4]; + out[j++] = BASE64_PAD; + out[j++] = BASE64_PAD; + break; + case 2: + out[j++] = base64en[(l & 0xF) << 2]; + out[j++] = BASE64_PAD; + break; + } + + out[j] = 0; + + return j; +} + +unsigned int +base64_decode(const char *in, unsigned int inlen, unsigned char *out) +{ + unsigned int i; + unsigned int j; + unsigned char c; + + if (inlen & 0x3) { + return 0; + } + + for (i = j = 0; i < inlen; i++) { + if (in[i] == BASE64_PAD) { + break; + } + if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST) { + return 0; + } + + c = base64de[(unsigned char)in[i]]; + if (c == 255) { + return 0; + } + + switch (i & 0x3) { + case 0: + out[j] = (c << 2) & 0xFF; + break; + case 1: + out[j++] |= (c >> 4) & 0x3; + out[j] = (c & 0xF) << 4; + break; + case 2: + out[j++] |= (c >> 2) & 0xF; + out[j] = (c & 0x3) << 6; + break; + case 3: + out[j++] |= c; + break; + } + } + + return j; +} \ No newline at end of file From 58c3f858397473259bf94f170f03a995d03fa138 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 03:23:43 -0400 Subject: [PATCH 03/10] * NEW [codec[ Add codec and property parse for auth msg. Signed-off-by: wanghaemq --- src/supplemental/mqtt/mqtt_codec.c | 77 ++++++++++++++++++++++++++---- src/supplemental/mqtt/mqtt_msg.c | 42 ++++++++++++++++ src/supplemental/mqtt/mqtt_msg.h | 15 ++++++ 3 files changed, 126 insertions(+), 8 deletions(-) diff --git a/src/supplemental/mqtt/mqtt_codec.c b/src/supplemental/mqtt/mqtt_codec.c index 55c996a7..2dd277dd 100644 --- a/src/supplemental/mqtt/mqtt_codec.c +++ b/src/supplemental/mqtt/mqtt_codec.c @@ -38,6 +38,7 @@ static int nni_mqttv5_msg_encode_unsubscribe(nni_msg *); static int nni_mqttv5_msg_encode_unsuback(nni_msg *); static int nni_mqttv5_msg_encode_base(nni_msg *); static int nni_mqttv5_msg_encode_disconnect(nni_msg *); +static int nni_mqttv5_msg_encode_auth(nni_msg *); static int nni_mqtt_msg_decode_fixed_header(nni_msg *); static int nni_mqtt_msg_decode_connect(nni_msg *); @@ -65,7 +66,8 @@ static int nni_mqttv5_msg_decode_pubcomp(nni_msg *); static int nni_mqttv5_msg_decode_unsubscribe(nni_msg *); static int nni_mqttv5_msg_decode_unsuback(nni_msg *); static int nni_mqttv5_msg_decode_base(nni_msg *); -static int nni_mqttv5_msg_decode_disconnect(nni_msg *); +static int nni_mqttv5_msg_decode_disconnect(nni_msg *); +static int nni_mqttv5_msg_decode_auth(nni_msg *); static void destory_connect(nni_mqtt_proto_data *); static void destory_publish(nni_mqtt_proto_data *); @@ -147,7 +149,9 @@ static mqtt_msg_codec_handler codec_v5_handler[] = { { NNG_MQTT_PINGRESP, nni_mqttv5_msg_encode_base, nni_mqttv5_msg_decode_base }, { NNG_MQTT_DISCONNECT, nni_mqttv5_msg_encode_disconnect, - nni_mqttv5_msg_decode_disconnect } + nni_mqttv5_msg_decode_disconnect }, + { NNG_MQTT_AUTH, nni_mqttv5_msg_encode_auth, + nni_mqttv5_msg_decode_auth } }; int @@ -409,9 +413,11 @@ mqtt_msg_content_free(nni_mqtt_proto_data *mqtt) } break; - // TODO case NNG_MQTT_AUTH: - // - // break; + case NNG_MQTT_AUTH: + if (mqtt->var_header.auth.properties) { + property_free(mqtt->var_header.auth.properties); + } + break; default: break; @@ -554,6 +560,13 @@ nni_mqtt_msg_dup(void **dest, const void *src) } break; + case NNG_MQTT_AUTH: + if (s->var_header.auth.properties) { + property_dup(&mqtt->var_header.auth.properties, + s->var_header.auth.properties); + } + break; + default: break; } @@ -600,7 +613,7 @@ dup_subscribe(nni_mqtt_proto_data *dest, nni_mqtt_proto_data *src) for (size_t i = 0; i < src->payload.subscribe.topic_count; i++) { nni_mqtt_topic_qos_array_set(dest->payload.subscribe.topic_arr, i, (const char *) src->payload.subscribe.topic_arr[i].topic.buf, - src->payload.subscribe.topic_arr[i].topic.length, + src->payload.subscribe.topic_arr[i].topic.length, src->payload.subscribe.topic_arr[i].qos, src->payload.subscribe.topic_arr[i].nolocal, src->payload.subscribe.topic_arr[i].rap, @@ -1611,6 +1624,25 @@ nni_mqttv5_msg_encode_disconnect(nni_msg *msg) return MQTT_SUCCESS; } +static int +nni_mqttv5_msg_encode_auth(nni_msg *msg) +{ + nni_mqtt_proto_data *mqtt = nni_msg_get_proto_data(msg); + nni_msg_clear(msg); + + mqtt_auth_vhdr *var_header = &mqtt->var_header.auth; + nni_mqtt_msg_append_u8(msg, var_header->reason_code); + if (NULL == var_header->properties) { + mqtt->fixed_header.remaining_length = 2; + } else { + encode_properties(msg, mqtt->var_header.auth.properties, CMD_AUTH_V5); + mqtt->fixed_header.remaining_length = nng_msg_len(msg); + } + + nni_mqtt_msg_encode_fixed_header(msg, mqtt); + return MQTT_SUCCESS; +} + static int nni_mqtt_msg_decode_fixed_header(nni_msg *msg) @@ -1707,6 +1739,7 @@ nni_mqtt_msg_decode_connect(nni_msg *msg) return MQTT_ERR_PROTOCOL; } } + return MQTT_SUCCESS; } @@ -1875,6 +1908,34 @@ nni_mqttv5_msg_decode_disconnect(nni_msg *msg) return MQTT_SUCCESS; } +static int +nni_mqttv5_msg_decode_auth(nni_msg *msg) +{ + int ret; + nni_mqtt_proto_data *mqtt = nni_msg_get_proto_data(msg); + + uint8_t *body = nni_msg_body(msg); + size_t length = nni_msg_len(msg); + + struct pos_buf buf; + buf.curpos = &body[0]; + buf.endpos = &body[length]; + + ret = read_byte(&buf, &mqtt->var_header.auth.reason_code); + if (ret != MQTT_SUCCESS) { + return MQTT_ERR_PROTOCOL; + } + + /* Properties */ + uint32_t pos = buf.curpos - &body[0]; + uint32_t prop_len = 0; + mqtt->var_header.auth.properties = + decode_buf_properties(body, length, &pos, &prop_len, true); + buf.curpos = &body[0] + pos; + + return MQTT_SUCCESS; +} + static int nni_mqtt_msg_decode_connack(nni_msg *msg) { @@ -4245,5 +4306,5 @@ mqtt_get_next_packet_id(nni_atomic_int *id) if ((nni_atomic_get(id) & 0xFFFF) == 0) { nni_atomic_set(id, 1); } - return (uint16_t)packet_id & 0xFFFF; -} \ No newline at end of file + return (uint16_t)(packet_id & 0xFFFF); +} diff --git a/src/supplemental/mqtt/mqtt_msg.c b/src/supplemental/mqtt/mqtt_msg.c index e991fa8c..2c748bc2 100644 --- a/src/supplemental/mqtt/mqtt_msg.c +++ b/src/supplemental/mqtt/mqtt_msg.c @@ -740,6 +740,13 @@ nni_mqtt_msg_get_connect_user_name(nni_msg *msg) return (const char *) proto_data->payload.connect.user_name.buf; } +uint32_t +nni_mqtt_msg_get_connect_user_name_len(nni_msg *msg) +{ + nni_mqtt_proto_data *proto_data = nni_msg_get_proto_data(msg); + return proto_data->payload.connect.user_name.length; +} + const char * nni_mqtt_msg_get_connect_password(nni_msg *msg) { @@ -747,6 +754,13 @@ nni_mqtt_msg_get_connect_password(nni_msg *msg) return (const char *) proto_data->payload.connect.password.buf; } +uint32_t +nni_mqtt_msg_get_connect_password_len(nni_msg *msg) +{ + nni_mqtt_proto_data *proto_data = nni_msg_get_proto_data(msg); + return proto_data->payload.connect.password.length; +} + void nni_mqtt_msg_set_connack_return_code(nni_msg *msg, uint8_t code) { @@ -796,6 +810,34 @@ nni_mqtt_msg_get_disconnect_property(nng_msg *msg) return mqtt->var_header.disconnect.properties; } +property * +nni_mqtt_msg_get_auth_property(nni_msg *msg) +{ + nni_mqtt_proto_data *proto_data = nni_msg_get_proto_data(msg); + return proto_data->var_header.auth.properties; +} + +void +nni_mqtt_msg_set_auth_property(nni_msg *msg, property *prop) +{ + nni_mqtt_proto_data *proto_data = nni_msg_get_proto_data(msg); + proto_data->var_header.auth.properties = prop; +} + +uint8_t +nni_mqtt_msg_get_auth_reason_code(nni_msg *msg) +{ + nni_mqtt_proto_data *proto_data = nni_msg_get_proto_data(msg); + return proto_data->var_header.auth.reason_code; +} + +void +nni_mqtt_msg_set_auth_reason_code(nni_msg *msg, uint8_t reason_code) +{ + nni_mqtt_proto_data *proto_data = nni_msg_get_proto_data(msg); + proto_data->var_header.auth.reason_code = reason_code; +} + void nni_mqtt_msg_dump( nni_msg *msg, uint8_t *buffer, uint32_t len, bool print_bytes) diff --git a/src/supplemental/mqtt/mqtt_msg.h b/src/supplemental/mqtt/mqtt_msg.h index b80d88f3..59d9eee9 100644 --- a/src/supplemental/mqtt/mqtt_msg.h +++ b/src/supplemental/mqtt/mqtt_msg.h @@ -143,6 +143,12 @@ typedef struct mqtt_unsuback_vhdr_t { property *properties; } mqtt_unsuback_vhdr; +typedef struct mqtt_auth_vhdr_t { + uint8_t reason_code; + // MQTTV5 + property *properties; +} mqtt_auth_vhdr; + /***************************************************************************** * Union to cover all Variable Header types ****************************************************************************/ @@ -159,6 +165,7 @@ union mqtt_variable_header { mqtt_suback_vhdr suback; mqtt_unsubscribe_vhdr unsubscribe; mqtt_unsuback_vhdr unsuback; + mqtt_auth_vhdr auth; }; /***************************************************************************** @@ -336,7 +343,9 @@ NNG_DECL void nni_mqtt_msg_set_connect_will_property(nni_msg *, property *); NNG_DECL uint8_t nni_mqtt_msg_get_connect_proto_version(nni_msg *); NNG_DECL uint16_t nni_mqtt_msg_get_connect_keep_alive(nni_msg *); NNG_DECL const char *nni_mqtt_msg_get_connect_user_name(nni_msg *); +NNG_DECL uint32_t nni_mqtt_msg_get_connect_user_name_len(nni_msg *); NNG_DECL const char *nni_mqtt_msg_get_connect_password(nni_msg *); +NNG_DECL uint32_t nni_mqtt_msg_get_connect_password_len(nni_msg *); NNG_DECL const char *nni_mqtt_msg_get_connect_client_id(nni_msg *); NNG_DECL const char *nni_mqtt_msg_get_connect_will_topic(nni_msg *); NNG_DECL bool nni_mqtt_msg_get_connect_will_retain(nni_msg *); @@ -439,6 +448,12 @@ NNG_DECL void nni_mqtt_msg_set_disconnect_reason_code(nng_msg *msg, uint8_t reas NNG_DECL property *nni_mqtt_msg_get_disconnect_property(nng_msg *msg); NNG_DECL void nni_mqtt_msg_set_disconnect_property(nng_msg *msg, property *prop); +// mqtt auth +NNG_DECL property *nni_mqtt_msg_get_auth_property(nng_msg *msg); +NNG_DECL uint8_t nni_mqtt_msg_get_auth_reason_code(nng_msg *msg); +NNG_DECL void nni_mqtt_msg_set_auth_property(nng_msg *msg, property *prop); +NNG_DECL void nni_mqtt_msg_set_auth_reason_code(nng_msg *msg, uint8_t reason_code); + NNG_DECL void nni_mqtt_msg_dump(nni_msg *, uint8_t *, uint32_t, bool); // mqtt topic create/free NNG_DECL nni_mqtt_topic *nni_mqtt_topic_array_create(size_t n); From 9e3ebebd41cfcd309dcead68f401ea8eb0bce426 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 03:24:33 -0400 Subject: [PATCH 04/10] * NEW [mqtt_tcp] Handle auth and scram msgs in mqtt tcp layer. Signed-off-by: wanghaemq --- src/mqtt/transport/tcp/mqtt_tcp.c | 215 +++++++++++++++++++++++++++--- 1 file changed, 197 insertions(+), 18 deletions(-) diff --git a/src/mqtt/transport/tcp/mqtt_tcp.c b/src/mqtt/transport/tcp/mqtt_tcp.c index 6b1542d3..e9f80f15 100644 --- a/src/mqtt/transport/tcp/mqtt_tcp.c +++ b/src/mqtt/transport/tcp/mqtt_tcp.c @@ -17,6 +17,13 @@ #include "nng/supplemental/nanolib/log.h" #include "supplemental/mqtt/mqtt_msg.h" +#ifdef SUPP_SCRAM +#define SCRAM_ITERATION_CNT_DEFAULT 4096 +#define SCRAM_DIGEST_DEFAULT SCRAM_SHA256 +#define SCRAM_DIGEST_STR_DEFAULT "SCRAM-SHA-256" +#include "supplemental/scram/scram.h" +#endif + // TCP transport. Platform specific TCP operations must be // supplied as well. @@ -84,6 +91,10 @@ struct mqtt_tcptran_ep { nni_dialer * ndialer; void * property; // property void * connmsg; +#ifdef SUPP_SCRAM + void * scram_ctx; + nni_msg * authmsg; +#endif #ifdef NNG_ENABLE_STATS nni_stat_item st_rcv_max; @@ -151,8 +162,8 @@ static int mqtt_tcptran_pipe_init(void *arg, nni_pipe *npipe) { mqtt_tcptran_pipe *p = arg; - p->npipe = npipe; + p->npipe = npipe; // nni_lmq_init(&p->rslmq, 10240); p->busy = false; // set max value by default @@ -299,14 +310,15 @@ mqtt_tcptran_pipe_nego_cb(void *arg) nni_mtx_unlock(&ep->mtx); return; } - // only accept CONNACK msg - if ((p->rxlen[0] & CMD_CONNACK) != CMD_CONNACK) { + // only accept CONNACK/AUTH msg + if (((p->rxlen[0] & CMD_CONNACK) != CMD_CONNACK) && + ((p->rxlen[0] & CMD_AUTH_V5) != CMD_AUTH_V5)) { + log_error("Invalid type received %x %x", p->rxlen[0], p->rxlen[1]); rv = PROTOCOL_ERROR; goto error; } // finish recevied fixed header if (p->rxmsg == NULL) { - pos = 0; if ((rv = mqtt_get_remaining_length(p->rxlen, p->gotrxhead, (uint32_t *) &var_int, &pos)) != 0) { @@ -339,7 +351,7 @@ mqtt_tcptran_pipe_nego_cb(void *arg) nni_mtx_unlock(&ep->mtx); return; } - // Connack + // Handle connack/auth if (p->gotrxhead >= p->wantrxhead) { if (p->proto == MQTT_PROTOCOL_VERSION_v5) { rv = nni_mqttv5_msg_decode(p->rxmsg); @@ -347,10 +359,95 @@ mqtt_tcptran_pipe_nego_cb(void *arg) if (rv != 0) goto mqtt_error; property_free(ep->property); - property *prop = (void *)nni_mqtt_msg_get_connack_property(p->rxmsg); - if (property_dup((property **)&ep->property, prop) != 0) { - goto mqtt_error; +#ifdef SUPP_SCRAM + if (ep->scram_ctx && + nni_mqtt_msg_get_packet_type(p->rxmsg) == NNG_MQTT_AUTH) { + property *prop = nni_mqtt_msg_get_auth_property(p->rxmsg); + if (prop == NULL) { + ep->reason_code = MQTT_ERR_MALFORMED; + rv = MQTT_ERR_PROTOCOL; + log_error("No property found in AUTH msg"); + goto mqtt_error; + } + uint8_t rc = nni_mqtt_msg_get_auth_reason_code(p->rxmsg); + if (rc != 0x18) { + ep->reason_code = MQTT_ERR_MALFORMED; + rv = MQTT_ERR_PROTOCOL; + log_error("Reason code in AUTH msg is invalid"); + goto mqtt_error; + } + property_data *data = property_get_value(prop, AUTHENTICATION_DATA); + if (data == NULL || data->p_value.str.buf == NULL) { + ep->reason_code = MQTT_ERR_MALFORMED; + rv = MQTT_ERR_PROTOCOL; + log_error("No auth data property found in AUTH msg"); + goto mqtt_error; + } + log_debug("auth:server_first_msg:%.*s", + data->p_value.str.length, (char *)data->p_value.str.buf); + char *client_final_msg = scram_handle_server_first_msg( + ep->scram_ctx, (char *)data->p_value.str.buf, data->p_value.str.length); + if (client_final_msg == NULL) { + ep->reason_code = MQTT_ERR_MALFORMED; + rv = MQTT_ERR_PROTOCOL; + log_error("Error in handle scram server_first_msg"); + goto mqtt_error; + } + log_debug("auth:client_final_msg:%s", client_final_msg); + // TODO 0x19 Re-authenticate + // Prepare authmsg with client_final_msg + nni_msg *authmsg; + nni_mqtt_msg_alloc(&authmsg, 0); + nni_mqtt_msg_set_packet_type(authmsg, NNG_MQTT_AUTH); + nni_mqtt_msg_set_auth_reason_code(authmsg, 0x18); + property *props = mqtt_property_alloc(); + property *prop_auth_method = property_set_value_str( + AUTHENTICATION_METHOD, SCRAM_DIGEST_STR_DEFAULT, + strlen(SCRAM_DIGEST_STR_DEFAULT), true); + property *prop_auth_data = property_set_value_str( + AUTHENTICATION_DATA, client_final_msg, strlen(client_final_msg), true); + property_append(props, prop_auth_method); + property_append(props, prop_auth_data); + nni_mqtt_msg_set_auth_property(authmsg, props); + if (0 != nni_mqttv5_msg_encode(authmsg)) { + ep->reason_code = MQTT_ERR_MALFORMED; + rv = MQTT_ERR_PROTOCOL; + log_error("Error in encode auth msg with client_final_msg"); + goto mqtt_error; + } + if (ep->authmsg) + nng_msg_free(ep->authmsg); + ep->authmsg = authmsg; + nng_free(client_final_msg, 0); + // Update got/want to send client_final_msg and recv connack + nng_msg_free(p->rxmsg); + p->gotrxhead = 0; + p->gottxhead = 0; + p->wantrxhead = 2; + p->wanttxhead = nni_msg_header_len(authmsg) + nni_msg_len(authmsg); + p->rxmsg = NULL; + + nni_iov iov[2]; + int niov = 0; + if (nni_msg_header_len(authmsg) > 0) { + iov[niov].iov_buf = nni_msg_header(authmsg); + iov[niov].iov_len = nni_msg_header_len(authmsg); + niov++; + } + if (nni_msg_len(authmsg) > 0) { + iov[niov].iov_buf = nni_msg_body(authmsg); + iov[niov].iov_len = nni_msg_len(authmsg); + niov++; + } + nni_aio_set_iov(aio, niov, iov); + nng_stream_send(p->conn, p->negoaio); + nni_mtx_unlock(&ep->mtx); + + return; } +#endif + property *prop = nni_mqtt_msg_get_connack_property(p->rxmsg); + property_dup((property **)&ep->property, prop); property_data *data; data = property_get_value(ep->property, RECEIVE_MAXIMUM); if (data) { @@ -370,13 +467,40 @@ mqtt_tcptran_pipe_nego_cb(void *arg) goto mqtt_error; } else { p->packmax = data->p_value.u32; - log_error("Set max packet size as %ld", p->packmax); + log_info("Set max packet size as %ld", p->packmax); } } data = property_get_value(ep->property, PUBLISH_MAXIMUM_QOS); if (data) { p->qosmax = data->p_value.u8; } +#ifdef SUPP_SCRAM + data = property_get_value(ep->property, AUTHENTICATION_DATA); + if (data && data->p_value.str.buf && ep->scram_ctx) { + char *server_final_msg = (char *)data->p_value.str.buf; + log_debug("auth:server_final_msg:%.*s", + data->p_value.str.length, server_final_msg); + char *result = scram_handle_server_final_msg( + ep->scram_ctx, server_final_msg, data->p_value.str.length); + if (result == NULL) { + log_error("Enhanced Authentication failed"); + rv = MQTT_ERR_PROTOCOL; + ep->reason_code = rv; + // Failed so closed the connection + goto error; + } else { + log_info("Enhanced Authentication Passed"); + } + } else if (ep->scram_ctx) { + // We want a authenticate response. but not found + log_error("Enhanced Authentication failed"); + rv = MQTT_ERR_PROTOCOL; + ep->reason_code = rv; + goto error; + } else { + // No more action + } +#endif // TODO move CONNACK to protocol layer // data = property_get_value(ep->property, SERVER_KEEP_ALIVE); @@ -765,16 +889,16 @@ mqtt_tcptran_pipe_send_start(mqtt_tcptran_pipe *p) p->qosmax == 1? (*header &= 0XF9) & (*header |= 0X02): NNI_ARG_UNUSED(*header); p->qosmax == 0? *header &= 0XF9:*header; } - - } - // check max packet size - if (nni_msg_header_len(msg) + nni_msg_len(msg) > p->packmax) { - txaio = p->txaio; - nni_aio_finish_error(txaio, UNSPECIFIED_ERROR); - return; } } + // check max packet size + if (nni_msg_header_len(msg) + nni_msg_len(msg) > p->packmax) { + txaio = p->txaio; + nni_aio_finish_error(txaio, UNSPECIFIED_ERROR); + return; + } + txaio = p->txaio; niov = 0; @@ -944,9 +1068,9 @@ mqtt_tcptran_pipe_start( p->conn = conn; p->ep = ep; - p->rcvmax = 0; p->qosmax = 0; p->packmax = 0; + p->rcvmax = 0; p->sndmax = 65535; nni_dialer_getopt(ep->ndialer, NNG_OPT_MQTT_CONNMSG, &connmsg, NULL, @@ -962,6 +1086,48 @@ mqtt_tcptran_pipe_start( rv = nni_mqtt_msg_encode(connmsg); else if (mqtt_version == MQTT_PROTOCOL_VERSION_v5) { property *prop = nni_mqtt_msg_get_connect_property(connmsg); +#ifdef SUPP_SCRAM + if (prop == NULL) + prop = mqtt_property_alloc(); + char *pwd = NULL, *username = NULL; + char *pwd2 = NULL, *username2 = NULL; + int pwdsz, usernamesz; + if (((pwd = (char *)nni_mqtt_msg_get_connect_password(connmsg)) != NULL) && + ((username = (char *)nni_mqtt_msg_get_connect_user_name(connmsg)) != NULL)) { + pwdsz = nni_mqtt_msg_get_connect_password_len(connmsg); + usernamesz = nni_mqtt_msg_get_connect_user_name_len(connmsg); + pwd2 = strndup(pwd, pwdsz); + username2 = strndup(username, usernamesz); + if (ep->scram_ctx) { + scram_ctx_free(ep->scram_ctx); + } + ep->scram_ctx = scram_ctx_create(pwd2, strlen(pwd2), + SCRAM_ITERATION_CNT_DEFAULT, SCRAM_DIGEST_DEFAULT, 0); + } + if (ep->scram_ctx) { + property *prop_auth_method = property_set_value_str( + AUTHENTICATION_METHOD, SCRAM_DIGEST_STR_DEFAULT, + strlen(SCRAM_DIGEST_STR_DEFAULT), true); + char *client_first_msg = scram_client_first_msg(ep->scram_ctx, username2); + property *prop_auth_data = property_set_value_str( + AUTHENTICATION_DATA, client_first_msg, strlen(client_first_msg), true); + property_append(prop, prop_auth_method); + property_append(prop, prop_auth_data); + nni_mqtt_msg_set_connect_property(connmsg, prop); + prop = NULL; + log_info("auth:client_first_msg:%s", client_first_msg); + //property_free(prop_auth_method); + //property_free(prop_auth_data); + } + if (pwd2) + nng_free(pwd2, 0); + if (username2) + nng_free(username2, 0); + if (prop) { + mqtt_property_free(prop); + prop = NULL; + } +#endif property_data *data; data = property_get_value(prop, MAXIMUM_PACKET_SIZE); if (data) @@ -1024,6 +1190,15 @@ mqtt_tcptran_ep_fini(void *arg) return; } nni_mtx_unlock(&ep->mtx); + +#ifdef SUPP_SCRAM + if (ep->authmsg) + nni_msg_free(ep->authmsg); + ep->authmsg = NULL; + if (ep->scram_ctx) + scram_ctx_free(ep->scram_ctx); +#endif + nni_aio_stop(ep->timeaio); nni_aio_stop(ep->connaio); nng_stream_dialer_free(ep->dialer); @@ -1294,7 +1469,11 @@ mqtt_tcptran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer) if ((rv = mqtt_tcptran_ep_init(&ep, url, sock)) != 0) { return (rv); } - ep->ndialer = ndialer; + ep->ndialer = ndialer; +#ifdef SUPP_SCRAM + ep->scram_ctx = NULL; + ep->authmsg = NULL; +#endif if ((rv != 0) || ((rv = nni_aio_alloc(&ep->connaio, mqtt_tcptran_dial_cb, ep)) != From dd57c7469e39d2c267eb5165539990b5b5164536 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 03:25:51 -0400 Subject: [PATCH 05/10] * NEW [cmake] Add base64 and scram to cmake. Signed-off-by: wanghaemq --- CMakeLists.txt | 6 +++++- cmake/NNGOptions.cmake | 6 ++++++ src/supplemental/nanolib/CMakeLists.txt | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cac1c4d..46cb3410 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -301,12 +301,16 @@ endif () if(NNG_ENABLE_QUIC) set(NNG_PROTO_MQTT_QUIC_CLIENT ON) add_definitions(-DSUPP_QUIC) +endif() + +if (NNG_ENABLE_SCRAM) + add_definitions(-DSUPP_SCRAM) endif () add_subdirectory(src) if (NNG_TESTS) - add_subdirectory(tests) + add_subdirectory(tests) endif () # Build the tools diff --git a/cmake/NNGOptions.cmake b/cmake/NNGOptions.cmake index 4d56ad9c..acd289de 100644 --- a/cmake/NNGOptions.cmake +++ b/cmake/NNGOptions.cmake @@ -109,6 +109,12 @@ else () set(NNG_QUIC_LIB none) endif () +option(NNG_ENABLE_SCRAM "Enable SCRAM support." OFF) +if (NNG_ENABLE_SCRAM) + set(SUPP_SCRAM ON) +endif () + + # TLS support. # Enabling TLS is required to enable support for the TLS transport diff --git a/src/supplemental/nanolib/CMakeLists.txt b/src/supplemental/nanolib/CMakeLists.txt index 7f85e948..2bb99f98 100644 --- a/src/supplemental/nanolib/CMakeLists.txt +++ b/src/supplemental/nanolib/CMakeLists.txt @@ -6,5 +6,5 @@ # found online at https://opensource.org/licenses/MIT. # -nng_sources(file.c log.c) -nng_headers(nng/supplemental/nanolib/file.h nng/supplemental/nanolib/log.h) +nng_sources(file.c log.c base64.c) +nng_headers(nng/supplemental/nanolib/file.h nng/supplemental/nanolib/log.h nng/supplemental/nanolib/base64.h) From 589c25f57a703e2a69c7a3ec4951bbecfe2df0c3 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 03:39:30 -0400 Subject: [PATCH 06/10] * NEW [cmake] Update cmake to load ssl when scram is enabled. Signed-off-by: wanghaemq --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46cb3410..43f3a9f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,6 +305,8 @@ endif() if (NNG_ENABLE_SCRAM) add_definitions(-DSUPP_SCRAM) + find_package(OpenSSL) + target_link_libraries(nng PRIVATE OpenSSL::SSL OpenSSL::Crypto) endif () add_subdirectory(src) From 20033e0b2157e26d506aa9c529865f8cf91ac25c Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 04:45:15 -0400 Subject: [PATCH 07/10] * NEW [mqtt_tcp] Add option to enable/disable scram. Signed-off-by: wanghaemq --- include/nng/mqtt/mqtt_client.h | 2 ++ src/mqtt/transport/tcp/mqtt_tcp.c | 37 ++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/include/nng/mqtt/mqtt_client.h b/include/nng/mqtt/mqtt_client.h index f4d28a90..2f486f33 100644 --- a/include/nng/mqtt/mqtt_client.h +++ b/include/nng/mqtt/mqtt_client.h @@ -71,6 +71,8 @@ extern "C" { #define NNG_OPT_MQTT_SQLITE "mqtt-sqlite-option" +#define NNG_OPT_MQTT_ENABLE_SCRAM "mqtt-scram-option" + // NNG_OPT_MQTT_QOS is a byte (only lower two bits significant) representing // the quality of service. At this time, only level zero is supported. // TODO: level 1 and level 2 QoS diff --git a/src/mqtt/transport/tcp/mqtt_tcp.c b/src/mqtt/transport/tcp/mqtt_tcp.c index e9f80f15..f9f70418 100644 --- a/src/mqtt/transport/tcp/mqtt_tcp.c +++ b/src/mqtt/transport/tcp/mqtt_tcp.c @@ -91,6 +91,7 @@ struct mqtt_tcptran_ep { nni_dialer * ndialer; void * property; // property void * connmsg; + bool enable_scram; #ifdef SUPP_SCRAM void * scram_ctx; nni_msg * authmsg; @@ -1092,8 +1093,9 @@ mqtt_tcptran_pipe_start( char *pwd = NULL, *username = NULL; char *pwd2 = NULL, *username2 = NULL; int pwdsz, usernamesz; - if (((pwd = (char *)nni_mqtt_msg_get_connect_password(connmsg)) != NULL) && - ((username = (char *)nni_mqtt_msg_get_connect_user_name(connmsg)) != NULL)) { + if (ep->enable_scram == true && + ((pwd = (char *)nni_mqtt_msg_get_connect_password(connmsg)) != NULL) && + ((username = (char *)nni_mqtt_msg_get_connect_user_name(connmsg)) != NULL)) { pwdsz = nni_mqtt_msg_get_connect_password_len(connmsg); usernamesz = nni_mqtt_msg_get_connect_user_name_len(connmsg); pwd2 = strndup(pwd, pwdsz); @@ -1469,10 +1471,11 @@ mqtt_tcptran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer) if ((rv = mqtt_tcptran_ep_init(&ep, url, sock)) != 0) { return (rv); } - ep->ndialer = ndialer; + ep->ndialer = ndialer; + ep->enable_scram = false; #ifdef SUPP_SCRAM - ep->scram_ctx = NULL; - ep->authmsg = NULL; + ep->scram_ctx = NULL; + ep->authmsg = NULL; #endif if ((rv != 0) || @@ -1670,6 +1673,26 @@ mqtt_tcptran_ep_set_reconnect_backoff(void *arg, const void *v, size_t sz, nni_o return (rv); } +static int +mqtt_tcptran_ep_set_enable_scram(void *arg, const void *v, size_t sz, nni_opt_type t) +{ + mqtt_tcptran_ep *ep = arg; + bool tmp; + int rv; + + if ((rv = nni_copyin_bool(&tmp, v, sz, t)) == 0) { + nni_mtx_lock(&ep->mtx); + ep->enable_scram = tmp; +#ifdef SUPP_SCRAM + log_info("Auth SCRAM status: %s", tmp == 1 ? "Enabled":"Disabled"); +#else + log_warn("Auth SCRAM Error. Try to compile with NNG_ENABLE_SCRAM"); +#endif + nni_mtx_unlock(&ep->mtx); + } + return (rv); +} + static int mqtt_tcptran_ep_bind(void *arg) { @@ -1751,6 +1774,10 @@ static const nni_option mqtt_tcptran_ep_opts[] = { .o_name = NNG_OPT_URL, .o_get = mqtt_tcptran_ep_get_url, }, + { + .o_name = NNG_OPT_MQTT_ENABLE_SCRAM, + .o_set = mqtt_tcptran_ep_set_enable_scram, + }, // terminate list { .o_name = NULL, From 1d90ed67eb00f574f9c408c2ff2e0361ba16ca65 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 05:24:57 -0400 Subject: [PATCH 08/10] * FIX [demo] Fix some warnings in mqtt_client and mqttv5_client. Signed-off-by: wanghaemq --- demo/mqtt/mqtt_client.c | 20 +++++++++++++------- demo/mqttv5/mqttv5_client.c | 30 ++++++++++++++++++------------ 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/demo/mqtt/mqtt_client.c b/demo/mqtt/mqtt_client.c index 1d4b0c57..435345f1 100644 --- a/demo/mqtt/mqtt_client.c +++ b/demo/mqtt/mqtt_client.c @@ -53,6 +53,7 @@ int keepRunning = 1; void intHandler(int dummy) { + (void) dummy; keepRunning = 0; fprintf(stderr, "\nclient exit(0).\n"); // nng_closeall(); @@ -89,6 +90,8 @@ disconnect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) // nng_pipe_get_ptr(p, NNG_OPT_MQTT_DISCONNECT_PROPERTY, &prop); // nng_socket_get? printf("%s: disconnected!\n", __FUNCTION__); + (void) ev; + (void) arg; } static void @@ -101,6 +104,8 @@ connect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) // property *prop; // nng_pipe_get_ptr(p, NNG_OPT_MQTT_CONNECT_PROPERTY, &prop); printf("%s: connected!\n", __FUNCTION__); + (void) ev; + (void) arg; } // Connect to the given address. @@ -194,7 +199,6 @@ struct pub_params { void publish_cb(void *args) { - int rv; struct pub_params *params = args; do { client_publish(*params->sock, params->topic, params->data, @@ -228,6 +232,8 @@ sqlite_config(nng_socket *sock, uint8_t proto_ver) // set sqlite option pointer to socket return nng_socket_set_ptr(*sock, NNG_OPT_MQTT_SQLITE, sqlite); #else + (void) sock; + (void) proto_ver; return (0); #endif } @@ -237,24 +243,23 @@ send_callback (nng_mqtt_client *client, nng_msg *msg, void *arg) { nng_aio * aio = client->send_aio; uint32_t count; uint8_t * code; - uint8_t type; if (msg == NULL) return; switch (nng_mqtt_msg_get_packet_type(msg)) { case NNG_MQTT_SUBACK: - code = (reason_code *) nng_mqtt_msg_get_suback_return_codes( + code = nng_mqtt_msg_get_suback_return_codes( msg, &count); printf("SUBACK reason codes are"); - for (int i = 0; i < count; ++i) + for (int i = 0; i < (int)count; ++i) printf("%d ", code[i]); printf("\n"); break; case NNG_MQTT_UNSUBACK: - code = (reason_code *) nng_mqtt_msg_get_unsuback_return_codes( + code = nng_mqtt_msg_get_unsuback_return_codes( msg, &count); printf("UNSUBACK reason codes are"); - for (int i = 0; i < count; ++i) + for (int i = 0; i < (int)count; ++i) printf("%d ", code[i]); printf("\n"); break; @@ -268,6 +273,7 @@ send_callback (nng_mqtt_client *client, nng_msg *msg, void *arg) { printf("aio mqtt result %d \n", nng_aio_result(aio)); // printf("suback %d \n", *code); nng_msg_free(msg); + (void) arg; } int @@ -319,7 +325,7 @@ main(const int argc, const char **argv) params.interval = interval; params.verbose = verbose; - char thread_name[20]; + // char thread_name[20]; sqlite_config(params.sock, MQTT_PROTOCOL_VERSION_v311); diff --git a/demo/mqttv5/mqttv5_client.c b/demo/mqttv5/mqttv5_client.c index 630e4fa5..e34fe151 100644 --- a/demo/mqttv5/mqttv5_client.c +++ b/demo/mqttv5/mqttv5_client.c @@ -53,6 +53,7 @@ int keepRunning = 1; void intHandler(int dummy) { + (void) dummy; keepRunning = 0; fprintf(stderr, "\nclient exit(0).\n"); exit(0); @@ -88,6 +89,8 @@ disconnect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) // nng_pipe_get_ptr(p, NNG_OPT_MQTT_DISCONNECT_PROPERTY, &prop); // nng_socket_get? printf("%s: disconnected! RC [%d] \n", __FUNCTION__, reason); + (void) ev; + (void) arg; } static void @@ -100,6 +103,8 @@ connect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) // property *prop; // nng_pipe_get_ptr(p, NNG_OPT_MQTT_CONNECT_PROPERTY, &prop); printf("%s: connected! RC [%d] \n", __FUNCTION__, reason); + (void) ev; + (void) arg; } // Connect to the given address. @@ -216,25 +221,24 @@ send_callback(nng_mqtt_client *client, nng_msg *msg, void *arg) { nng_aio * aio = client->send_aio; uint32_t count; uint8_t * code; - uint8_t type; if (msg == NULL) return; switch (nng_mqtt_msg_get_packet_type(msg)) { case NNG_MQTT_SUBACK: - code = (reason_code *) nng_mqtt_msg_get_suback_return_codes( + code = nng_mqtt_msg_get_suback_return_codes( msg, &count); - printf("SUBACK reason codes are"); - for (int i = 0; i < count; ++i) - printf("%d ", code[i]); + printf("SUBACK reason codes are: "); + for (int i = 0; i < (int)count; ++i) + printf("[%d] ", code[i]); printf("\n"); break; case NNG_MQTT_UNSUBACK: - code = (reason_code *) nng_mqtt_msg_get_unsuback_return_codes( + code = nng_mqtt_msg_get_unsuback_return_codes( msg, &count); - printf("UNSUBACK reason codes are"); - for (int i = 0; i < count; ++i) - printf("%d ", code[i]); + printf("UNSUBACK reason codes are: "); + for (int i = 0; i < (int)count; ++i) + printf("[%d] ", code[i]); printf("\n"); break; case NNG_MQTT_PUBACK: @@ -244,9 +248,10 @@ send_callback(nng_mqtt_client *client, nng_msg *msg, void *arg) { printf("Sending in async way is done.\n"); break; } - printf("aio mqtt result %d \n", nng_aio_result(aio)); + printf("Aio mqtt result %d \n", nng_aio_result(aio)); // printf("suback %d \n", *code); nng_msg_free(msg); + (void) arg; } // Publish a message to the given topic and with the given QoS. @@ -343,7 +348,6 @@ void msg_recv_deal(nng_msg *msg, bool verbose) void publish_cb(void *args) { - int rv; struct pub_params *params = args; do { client_publish(*params->sock, params->topic, params->data, @@ -377,6 +381,8 @@ sqlite_config(nng_socket *sock, uint8_t proto_ver) // set sqlite option pointer to socket return nng_socket_set_ptr(*sock, NNG_OPT_MQTT_SQLITE, sqlite); #else + (void) sock; + (void) proto_ver; return (0); #endif } @@ -432,7 +438,7 @@ main(const int argc, const char **argv) params.interval = interval; params.verbose = verbose; - char thread_name[20]; + // char thread_name[20]; sqlite_config(params.sock, MQTT_PROTOCOL_VERSION_v5); From a42c8aa296392b7ab3b78c1a0366f88be3b97656 Mon Sep 17 00:00:00 2001 From: wanghaemq Date: Fri, 26 Jul 2024 05:25:45 -0400 Subject: [PATCH 09/10] * NEW [demo] Add demo mqttv5_scram.c. Signed-off-by: wanghaemq --- CMakeLists.txt | 1 + demo/mqttv5_scram/CMakeLists.txt | 33 +++ demo/mqttv5_scram/mqttv5_scram.c | 477 +++++++++++++++++++++++++++++++ 3 files changed, 511 insertions(+) create mode 100644 demo/mqttv5_scram/CMakeLists.txt create mode 100644 demo/mqttv5_scram/mqttv5_scram.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 43f3a9f4..e29067c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,6 +340,7 @@ if (BUILD_DEMO) add_subdirectory(demo/mqtt) add_subdirectory(demo/mqttv5) add_subdirectory(demo/mqtt_async) + add_subdirectory(demo/mqttv5_scram) add_subdirectory(demo/rest) add_subdirectory(demo/http_client) diff --git a/demo/mqttv5_scram/CMakeLists.txt b/demo/mqttv5_scram/CMakeLists.txt new file mode 100644 index 00000000..ba3db119 --- /dev/null +++ b/demo/mqttv5_scram/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# This software is supplied under the terms of the MIT License, a +# copy of which should be located in the distribution where this +# file was obtained (LICENSE.txt). A copy of the license may also be +# found online at https://opensource.org/licenses/MIT. + +cmake_minimum_required(VERSION 3.13) + +project(mqttv5_scram) + +if (BUILD_DEMO) +else () +# Call this from your own project's makefile. + find_package(nng CONFIG REQUIRED) +endif (BUILD_DEMO) + +find_package(Threads) + +if (DEBUG) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") + if (ASAN) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") + endif (ASAN) + if (TSAN) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") + endif (TSAN) +endif (DEBUG) + +add_executable(mqttv5_scram mqttv5_scram.c) +target_link_libraries(mqttv5_scram nng) +target_link_libraries(mqttv5_scram ${CMAKE_THREAD_LIBS_INIT}) + +target_compile_definitions(mqttv5_scram PRIVATE NNG_ELIDE_DEPRECATED) diff --git a/demo/mqttv5_scram/mqttv5_scram.c b/demo/mqttv5_scram/mqttv5_scram.c new file mode 100644 index 00000000..8c5f17bc --- /dev/null +++ b/demo/mqttv5_scram/mqttv5_scram.c @@ -0,0 +1,477 @@ +// Author: wangha +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +// +// This is just a simple MQTT client demonstration application. +// +// The application has two sub-commands: `pub` and `sub`. The `pub` +// sub-command publishes a given message to the server and then exits. +// The `sub` sub-command subscribes to the given topic filter and blocks +// waiting for incoming messages. +// +// # Example: +// +// Publish 'hello' to `topic` with QoS `0`: +// ``` +// $ ./mqtt_client pub mqtt-tcp://127.0.0.1:1883 0 topic hello +// ``` +// +// Subscribe to `topic` with QoS `0` and waiting for messages: +// ``` +// $ ./mqtt_client sub mqtt-tcp://127.0.0.1:1883 0 topic +// ``` +// + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// Subcommands +#define PUBLISH "pub" +#define SUBSCRIBE "sub" + +void +fatal(const char *msg, int rv) +{ + fprintf(stderr, "%s: %s\n", msg, nng_strerror(rv)); +} + +int keepRunning = 1; +void +intHandler(int dummy) +{ + (void) dummy; + keepRunning = 0; + fprintf(stderr, "\nclient exit(0).\n"); + exit(0); +} + +static void +disconnect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) +{ + int reason = 0; + // get connect reason + nng_pipe_get_int(p, NNG_OPT_MQTT_DISCONNECT_REASON, &reason); + // property *prop; + // nng_pipe_get_ptr(p, NNG_OPT_MQTT_DISCONNECT_PROPERTY, &prop); + // nng_socket_get? + printf("%s: disconnected! RC [%d] \n", __FUNCTION__, reason); + (void) ev; + (void) arg; +} + +static void +connect_cb(nng_pipe p, nng_pipe_ev ev, void *arg) +{ + int reason; + // get connect reason + nng_pipe_get_int(p, NNG_OPT_MQTT_CONNECT_REASON, &reason); + // get property for MQTT V5 + // property *prop; + // nng_pipe_get_ptr(p, NNG_OPT_MQTT_CONNECT_PROPERTY, &prop); + printf("%s: connected! RC [%d] \n", __FUNCTION__, reason); + (void) ev; + (void) arg; +} + +// Connect to the given address. +int +client_connect(nng_socket *sock, const char *url, bool verbose) +{ + nng_dialer dialer; + int rv; + + // To enable SCRAM open a MQTTv5 socket + if ((rv = nng_mqttv5_client_open(sock)) != 0) { + fatal("nng_socket", rv); + } + + if ((rv = nng_dialer_create(&dialer, *sock, url)) != 0) { + fatal("nng_dialer_create", rv); + } + // Enable scram + bool enable_scram = true; + nng_dialer_set(dialer, NNG_OPT_MQTT_ENABLE_SCRAM, &enable_scram, sizeof(bool)); + + // create a CONNECT message + /* CONNECT */ + nng_msg *connmsg; + nng_mqtt_msg_alloc(&connmsg, 0); + nng_mqtt_msg_set_packet_type(connmsg, NNG_MQTT_CONNECT); + // To enable SCRAM, version be MQTTv5 + nng_mqtt_msg_set_connect_proto_version(connmsg, 5); + nng_mqtt_msg_set_connect_keep_alive(connmsg, 600); + nng_mqtt_msg_set_connect_user_name(connmsg, "admin"); + nng_mqtt_msg_set_connect_password(connmsg, "public"); + nng_mqtt_msg_set_connect_will_msg( + connmsg, (uint8_t *) "bye-bye", strlen("bye-bye")); + nng_mqtt_msg_set_connect_will_topic(connmsg, "will_topic"); + nng_mqtt_msg_set_connect_clean_session(connmsg, true); + + property * p = mqtt_property_alloc(); + property *p1 = mqtt_property_set_value_u32(MAXIMUM_PACKET_SIZE, 120); + property *p2 = mqtt_property_set_value_u16(TOPIC_ALIAS_MAXIMUM, 65535); + mqtt_property_append(p, p1); + mqtt_property_append(p, p2); + nng_mqtt_msg_set_connect_property(connmsg, p); + + property *will_prop = mqtt_property_alloc(); + property *will_up = mqtt_property_set_value_strpair(USER_PROPERTY, + "user", strlen("user"), "pass", strlen("pass"), true); + mqtt_property_append(will_prop, will_up); + nng_mqtt_msg_set_connect_will_property(connmsg, will_prop); + + nng_mqtt_set_connect_cb(*sock, connect_cb, sock); + nng_mqtt_set_disconnect_cb(*sock, disconnect_cb, connmsg); + + uint8_t buff[1024] = { 0 }; + + if (verbose) { + nng_mqtt_msg_dump(connmsg, buff, sizeof(buff), true); + printf("%s\n", buff); + } + + printf("Connecting to server ...\n"); + nng_dialer_set_ptr(dialer, NNG_OPT_MQTT_CONNMSG, connmsg); + nng_dialer_start(dialer, NNG_FLAG_NONBLOCK); + + return (0); +} + +void print_property(property *prop) +{ + if (prop == NULL) { + return; + } + + // printf("%d \n", prop->id); + + uint8_t type = prop->data.p_type; + uint8_t prop_id = prop->id; + switch (type) { + case U8: + printf( + "id: %d, value: %d (U8)\n", prop_id, prop->data.p_value.u8); + break; + case U16: + printf("id: %d, value: %d (U16)\n", prop_id, + prop->data.p_value.u16); + break; + case U32: + printf("id: %d, value: %u (U32)\n", prop_id, + prop->data.p_value.u32); + break; + case VARINT: + printf("id: %d, value: %d (VARINT)\n", prop_id, + prop->data.p_value.varint); + break; + case BINARY: + printf("id: %d, value pointer: %p (BINARY)\n", prop_id, + prop->data.p_value.binary.buf); + break; + case STR: + printf("id: %d, value: %.*s (STR)\n", prop_id, + prop->data.p_value.str.length, + (const char *) prop->data.p_value.str.buf); + break; + case STR_PAIR: + printf("id: %d, value: '%.*s -> %.*s' (STR_PAIR)\n", prop_id, + prop->data.p_value.strpair.key.length, + prop->data.p_value.strpair.key.buf, + prop->data.p_value.strpair.value.length, + prop->data.p_value.strpair.value.buf); + break; + + default: + break; + } + +} + +static void +send_callback(nng_mqtt_client *client, nng_msg *msg, void *arg) { + nng_aio * aio = client->send_aio; + uint32_t count; + uint8_t * code; + + if (msg == NULL) + return; + switch (nng_mqtt_msg_get_packet_type(msg)) { + case NNG_MQTT_SUBACK: + code = nng_mqtt_msg_get_suback_return_codes( + msg, &count); + printf("SUBACK reason codes are: "); + for (int i = 0; i < (int)count; ++i) + printf("[%d] ", code[i]); + printf("\n"); + break; + case NNG_MQTT_UNSUBACK: + code = nng_mqtt_msg_get_unsuback_return_codes( + msg, &count); + printf("UNSUBACK reason codes are: "); + for (int i = 0; i < (int)count; ++i) + printf("[%d] ", code[i]); + printf("\n"); + break; + case NNG_MQTT_PUBACK: + printf("PUBACK"); + break; + default: + printf("Sending in async way is done.\n"); + break; + } + printf("Aio mqtt result %d \n", nng_aio_result(aio)); + // printf("suback %d \n", *code); + nng_msg_free(msg); + (void) arg; +} + +// Publish a message to the given topic and with the given QoS. +int +client_publish(nng_socket sock, const char *topic, uint8_t *payload, + uint32_t payload_len, uint8_t qos, bool verbose) +{ + int rv; + + // create a PUBLISH message + nng_msg *pubmsg; + nng_mqtt_msg_alloc(&pubmsg, 0); + nng_mqtt_msg_set_packet_type(pubmsg, NNG_MQTT_PUBLISH); + nng_mqtt_msg_set_publish_dup(pubmsg, 0); + nng_mqtt_msg_set_publish_qos(pubmsg, qos); + nng_mqtt_msg_set_publish_retain(pubmsg, 0); + nng_mqtt_msg_set_publish_payload( + pubmsg, (uint8_t *) payload, payload_len); + nng_mqtt_msg_set_publish_topic(pubmsg, topic); + + property *plist = mqtt_property_alloc(); + property *p1 = mqtt_property_set_value_u8(PAYLOAD_FORMAT_INDICATOR, 1); + mqtt_property_append(plist, p1); + property *p2 = mqtt_property_set_value_u16(TOPIC_ALIAS, 10); + mqtt_property_append(plist, p2); + property *p3 = mqtt_property_set_value_u32(MESSAGE_EXPIRY_INTERVAL, 10); + mqtt_property_append(plist, p3); + property *p4 = mqtt_property_set_value_str(RESPONSE_TOPIC, "aaaaaa", strlen("aaaaaa"), true); + mqtt_property_append(plist, p4); + property *p5 = mqtt_property_set_value_binary( + CORRELATION_DATA, (uint8_t *) "aaaaaa", strlen("aaaaaa"), true); + mqtt_property_append(plist, p5); + property *p6 = mqtt_property_set_value_strpair(USER_PROPERTY, "aaaaaa", strlen("aaaaaa"), "aaaaaa", strlen("aaaaaa"), true); + mqtt_property_append(plist, p6); + property *p7 = mqtt_property_set_value_str(CONTENT_TYPE, "aaaaaa", strlen("aaaaaa"), true); + mqtt_property_append(plist, p7); + + nng_mqtt_msg_set_publish_property(pubmsg, plist); + + if (verbose) { + uint8_t print[1024] = { 0 }; + nng_mqtt_msg_dump(pubmsg, print, 1024, true); + printf("%s\n", print); + } + + property *pl = nng_mqtt_msg_get_publish_property(pubmsg); + if (pl != NULL) { + mqtt_property_foreach(pl, print_property); + } + + printf("Publishing to '%s' ...\n", topic); + if ((rv = nng_sendmsg(sock, pubmsg, NNG_FLAG_NONBLOCK)) != 0) { + fatal("nng_sendmsg", rv); + } + + return rv; +} + +struct pub_params { + nng_socket *sock; + const char *topic; + uint8_t * data; + uint32_t data_len; + uint8_t qos; + bool verbose; + uint32_t interval; +}; + +void msg_recv_deal(nng_msg *msg, bool verbose) +{ + uint32_t topic_len = 0; + uint32_t payload_len = 0; + const char *topic = nng_mqtt_msg_get_publish_topic(msg, &topic_len); + char * payload = + (char *) nng_mqtt_msg_get_publish_payload( + msg, &payload_len); + + printf("Receive \'%.*s\' from \'%.*s\'\n", payload_len, payload, topic_len, topic); + property *pl = nng_mqtt_msg_get_publish_property(msg); + if (pl != NULL) { + mqtt_property_foreach(pl, print_property); + } + + if (verbose) { + uint8_t buff[1024] = { 0 }; + memset(buff, 0, sizeof(buff)); + nng_mqtt_msg_dump(msg, buff, sizeof(buff), true); + printf("%s\n", buff); + } + + nng_msg_free(msg); +} + +void +publish_cb(void *args) +{ + struct pub_params *params = args; + do { + client_publish(*params->sock, params->topic, params->data, + params->data_len, params->qos, params->verbose); + nng_msleep(params->interval); + } while (params->interval > 0); + printf("thread_exit\n"); +} + +struct pub_params params; + +int +main(const int argc, const char **argv) +{ + nng_socket sock; + + const char *exe = argv[0]; + + const char *cmd; + + if (5 == argc && 0 == strcmp(argv[1], SUBSCRIBE)) { + cmd = SUBSCRIBE; + } else if (6 <= argc && 0 == strcmp(argv[1], PUBLISH)) { + cmd = PUBLISH; + } else { + goto error; + } + + const char *url = argv[2]; + uint8_t qos = atoi(argv[3]); + const char *topic = argv[4]; + int rv = 0; + char * verbose_env = getenv("VERBOSE"); + bool verbose = verbose_env && strlen(verbose_env) > 0; + + nng_duration retry = 10000; + nng_socket_set_ms(sock, NNG_OPT_MQTT_RETRY_INTERVAL, retry); + client_connect(&sock, url, verbose); + nng_msleep(1000); + + signal(SIGINT, intHandler); + + if (strcmp(PUBLISH, cmd) == 0) { + const char *data = argv[5]; + uint32_t interval = 0; + uint32_t nthread = 1; + + if (argc >= 7) { + interval = atoi(argv[6]); + } + if (argc >= 8) { + nthread = atoi(argv[7]); + } + nng_thread *threads[nthread]; + + params.sock = &sock, params.topic = topic; + params.data = (uint8_t *) data; + params.data_len = strlen(data); + params.qos = qos; + params.interval = interval; + params.verbose = verbose; + + size_t i = 0; + for (i = 0; i < nthread; i++) { + nng_thread_create(&threads[i], publish_cb, ¶ms); + } + + for (i = 0; i < nthread; i++) { + nng_thread_destroy(threads[i]); + } + } else if (strcmp(SUBSCRIBE, cmd) == 0) { + nng_mqtt_topic_qos subscriptions[] = { + { + .qos = qos, + .topic = { + .buf = (uint8_t *) topic, + .length = strlen(topic), + }, + .nolocal = 1, + .rap = 1, + .retain_handling = 0, + }, + }; + nng_mqtt_topic unsubscriptions[] = { + { + .buf = (uint8_t *) topic, + .length = strlen(topic), + }, + }; + + property *plist = mqtt_property_alloc(); + mqtt_property_append(plist, + mqtt_property_set_value_varint( + SUBSCRIPTION_IDENTIFIER, 120)); + property *unsub_plist = NULL; + mqtt_property_dup(&unsub_plist, plist); + + // Sync subscription + // rv = nng_mqtt_subscribe(sock, subscriptions, 1, plist); + + // Asynchronous subscription + nng_mqtt_client *client = nng_mqtt_client_alloc(sock, &send_callback, NULL, true); + nng_mqtt_subscribe_async(client, subscriptions, + sizeof(subscriptions) / sizeof(nng_mqtt_topic_qos), plist); + + printf("Start receiving loop:\n"); + while (true) { + nng_msg *msg; + if ((rv = nng_recvmsg(sock, &msg, 0)) != 0) { + fatal("nng_recvmsg", rv); + continue; + } + + // we should only receive publish messages + assert(nng_mqtt_msg_get_packet_type(msg) == NNG_MQTT_PUBLISH); + msg_recv_deal(msg, verbose); + } + + // Sync unsubscription + // rv = nng_mqtt_unsubscribe(sock, subscriptions, 1, plist); + // Asynchronous unsubscription + nng_mqtt_unsubscribe_async(client, unsubscriptions, + sizeof(unsubscriptions) / sizeof(nng_mqtt_topic), + unsub_plist); + nng_mqtt_client_free(client, true); + } + + // disconnect + property *plist = mqtt_property_alloc(); + property *p = mqtt_property_set_value_strpair( + USER_PROPERTY, "aaa", strlen("aaa"), "aaa", strlen("aaa"), true); + mqtt_property_append(plist, p); + nng_mqtt_disconnect(&sock, 5, plist); + return 0; + +error: + fprintf(stderr, + "Usage: %s %s \n" + " %s %s \n", + exe, PUBLISH, exe, SUBSCRIBE); + return 1; +} From ea4ff5a9ec60ff8b8dc2963397697fa58ce9f338 Mon Sep 17 00:00:00 2001 From: jaylin Date: Mon, 29 Jul 2024 12:06:43 +0800 Subject: [PATCH 10/10] * MDF [mqtt_tcp/SCRAM-AUTH] add arbitary comments Signed-off-by: jaylin --- CMakeLists.txt | 5 +++-- src/supplemental/nanolib/base64.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e29067c1..6788047f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,8 +340,9 @@ if (BUILD_DEMO) add_subdirectory(demo/mqtt) add_subdirectory(demo/mqttv5) add_subdirectory(demo/mqtt_async) - add_subdirectory(demo/mqttv5_scram) - + if (NNG_ENABLE_SCRAM) + add_subdirectory(demo/mqttv5_scram) + endif() add_subdirectory(demo/rest) add_subdirectory(demo/http_client) add_subdirectory(demo/raw) diff --git a/src/supplemental/nanolib/base64.c b/src/supplemental/nanolib/base64.c index 3b4f9a09..aa3ce111 100644 --- a/src/supplemental/nanolib/base64.c +++ b/src/supplemental/nanolib/base64.c @@ -68,6 +68,7 @@ static const unsigned char base64de[] = { 49, 50, 51, 255, 255, 255, 255, 255 }; +// TODO replace it with nni_base64_decode unsigned int base64_encode(const unsigned char *in, unsigned int inlen, char *out) {