ext/libcouchbase/src/mcserver/negotiate.cc in libcouchbase-0.0.7 vs ext/libcouchbase/src/mcserver/negotiate.cc in libcouchbase-0.0.8

- old
+ new

@@ -13,10 +13,11 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ +#include <algorithm> #include "packetutils.h" #include "mcserver.h" #include "logging.h" #include "settings.h" #include <lcbio/lcbio.h> @@ -24,51 +25,17 @@ #include <lcbio/ssl.h> #include <cbsasl/cbsasl.h> #include "negotiate.h" #include "ctx-log-inl.h" -#include <string> -#include <sstream> -#include <vector> +using namespace lcb; -#define LOGARGS(ctx, lvl) ctx->sasl->settings, "negotiation", LCB_LOG_##lvl, __FILE__, __LINE__ -static void cleanup_negotiated(mc_pSESSINFO info); +#define LOGARGS(ctx, lvl) ctx->settings, "negotiation", LCB_LOG_##lvl, __FILE__, __LINE__ +static void cleanup_negotiated(SessionInfo* info); static void handle_ioerr(lcbio_CTX *ctx, lcb_error_t err); -static void handle_read(lcbio_CTX *ioctx, unsigned); #define SESSREQ_LOGFMT "<%s:%s> (SASLREQ=%p) " -/** - * Inner negotiation structure which is maintained as part of a 'protocol - * context'. - */ -struct mc_SESSINFO : public lcbio_PROTOCTX { - union { - cbsasl_secret_t secret; - char buffer[256]; - } u_auth; - - static mc_SESSINFO *get(void *arg) { - return reinterpret_cast<mc_SESSINFO*>(arg); - } - - mc_SESSINFO(lcb_settings *settings_); - bool setup(const lcbio_NAMEINFO& nistrs, const lcb_host_t& host, - const lcb::Authenticator& auth); - - ~mc_SESSINFO() { - if (sasl_client != NULL) { - cbsasl_dispose(&sasl_client); - sasl_client = NULL; - } - } - - cbsasl_conn_t *sasl_client; - std::string mech; - std::vector<uint16_t> server_features; - lcb_settings *settings; -}; - static void timeout_handler(void *arg); #define SESSREQ_LOGID(s) get_ctx_host(s->ctx), get_ctx_port(s->ctx), (void*)s static void @@ -82,32 +49,41 @@ /** * Structure used only for initialization. This is only used for the duration * of the request for negotiation and is deleted once negotiation has * completed (or failed). */ -struct mc_SESSREQ { - static mc_SESSREQ *get(void *arg) { - return reinterpret_cast<mc_SESSREQ*>(arg); +class lcb::SessionRequestImpl : public SessionRequest { +public: + static SessionRequestImpl *get(void *arg) { + return reinterpret_cast<SessionRequestImpl*>(arg); } - void start(lcbio_SOCKET *sock, lcb_settings *settings); + bool setup(const lcbio_NAMEINFO& nistrs, const lcb_host_t& host, + const lcb::Authenticator& auth); + void start(lcbio_SOCKET *sock); bool send_hello(); bool send_step(const lcb::MemcachedResponse& packet); bool read_hello(const lcb::MemcachedResponse& packet); + void send_auth(const char *sasl_data, unsigned ndata); + void handle_read(lcbio_CTX *ioctx); - mc_SESSREQ(lcbio_CONNDONE_cb callback, void *data, uint32_t timeout, - lcbio_TABLE *iot) + enum MechStatus { MECH_UNAVAILABLE, MECH_NOT_NEEDED, MECH_OK }; + MechStatus set_chosen_mech(std::string& mechlist, const char **data, unsigned int *ndata); + + SessionRequestImpl(lcbio_CONNDONE_cb callback, void *data, uint32_t timeout, lcbio_TABLE *iot, lcb_settings* settings_) : ctx(NULL), cb(callback), cbdata(data), timer(lcbio_timer_new(iot, this, timeout_handler)), - last_err(LCB_SUCCESS), sasl(NULL) { + last_err(LCB_SUCCESS), info(NULL), + settings(settings_) { if (timeout) { lcbio_timer_rearm(timer, timeout); } + memset(&u_auth, 0, sizeof(u_auth)); } - ~mc_SESSREQ(); + virtual ~SessionRequestImpl(); void cancel() { cb = NULL; delete this; } @@ -130,12 +106,12 @@ lcbio_SOCKET *s; lcbio_ctx_close(ctx, close_cb, &s); ctx = NULL; - lcbio_protoctx_add(s, sasl); - sasl = NULL; + lcbio_protoctx_add(s, info); + info = NULL; /** Invoke the callback, marking it a success */ cb(s, cbdata, LCB_SUCCESS, 0); lcbio_unref(s); @@ -151,23 +127,33 @@ bool has_error() const { return last_err != LCB_SUCCESS; } + union { + cbsasl_secret_t secret; + char buffer[256]; + } u_auth; + lcbio_CTX *ctx; lcbio_CONNDONE_cb cb; void *cbdata; lcbio_pTIMER timer; lcb_error_t last_err; - mc_pSESSINFO sasl; + cbsasl_conn_t *sasl_client; + SessionInfo* info; + lcb_settings *settings; }; +static void handle_read(lcbio_CTX *ioctx, unsigned) { + SessionRequestImpl::get(lcbio_ctx_data(ioctx))->handle_read(ioctx); +} static int sasl_get_username(void *context, int id, const char **result, unsigned int *len) { - mc_SESSINFO *ctx = mc_SESSINFO::get(context); + SessionRequestImpl *ctx = SessionRequestImpl::get(context); const char *u = NULL, *p = NULL; if (!context || !result || (id != CBSASL_CB_USER && id != CBSASL_CB_AUTHNAME)) { return SASL_BADPARAM; } @@ -182,32 +168,27 @@ static int sasl_get_password(cbsasl_conn_t *conn, void *context, int id, cbsasl_secret_t **psecret) { - struct mc_SESSINFO *ctx = mc_SESSINFO::get(context); + SessionRequestImpl *ctx = SessionRequestImpl::get(context); if (!conn || ! psecret || id != CBSASL_CB_PASS || ctx == NULL) { return SASL_BADPARAM; } *psecret = &ctx->u_auth.secret; return SASL_OK; } -mc_SESSINFO::mc_SESSINFO(lcb_settings *settings_) +SessionInfo::SessionInfo() { - sasl_client = NULL; - memset(&u_auth, 0, sizeof(u_auth)); - lcbio_PROTOCTX::id = LCBIO_PROTOCTX_SESSINFO; - lcbio_PROTOCTX::dtor = (void (*)(struct lcbio_PROTOCTX *))cleanup_negotiated; - - settings = settings_; + lcbio_PROTOCTX::dtor = (void (*)(lcbio_PROTOCTX *))cleanup_negotiated; } bool -mc_SESSINFO::setup(const lcbio_NAMEINFO& nistrs, const lcb_host_t& host, +SessionRequestImpl::setup(const lcbio_NAMEINFO& nistrs, const lcb_host_t& host, const lcb::Authenticator& auth) { cbsasl_callback_t sasl_callbacks[4]; sasl_callbacks[0].id = CBSASL_CB_USER; sasl_callbacks[0].proc = (int( *)(void)) &sasl_get_username; @@ -249,106 +230,102 @@ } static void timeout_handler(void *arg) { - mc_pSESSREQ sreq = mc_SESSREQ::get(arg); + SessionRequestImpl *sreq = SessionRequestImpl::get(arg); sreq->fail(LCB_ETIMEDOUT, "Negotiation timed out"); } /** * Called to retrive the mechlist from the packet. * @return 0 to continue authentication, 1 if no authentication needed, or * -1 on error. */ -static int -set_chosen_mech(mc_pSESSREQ sreq, std::string& mechlist, +SessionRequestImpl::MechStatus +SessionRequestImpl::set_chosen_mech(std::string& mechlist, const char **data, unsigned int *ndata) { cbsasl_error_t saslerr; - mc_pSESSINFO ctx = sreq->sasl; - - if (ctx->settings->sasl_mech_force) { - char *forcemech = ctx->settings->sasl_mech_force; + if (settings->sasl_mech_force) { + char *forcemech = settings->sasl_mech_force; if (mechlist.find(forcemech) == std::string::npos) { /** Requested mechanism not found */ - sreq->set_error(LCB_SASLMECH_UNAVAILABLE, mechlist.c_str()); - return -1; + set_error(LCB_SASLMECH_UNAVAILABLE, mechlist.c_str()); + return MECH_UNAVAILABLE; } mechlist.assign(forcemech); } const char *chosenmech; - saslerr = cbsasl_client_start(ctx->sasl_client, mechlist.c_str(), + saslerr = cbsasl_client_start(sasl_client, mechlist.c_str(), NULL, data, ndata, &chosenmech); switch (saslerr) { case SASL_OK: - ctx->mech.assign(chosenmech); - return 0; + info->mech.assign(chosenmech); + return MECH_OK; case SASL_NOMECH: - lcb_log(LOGARGS(sreq, INFO), SESSREQ_LOGFMT "Server does not support SASL (no mechanisms supported)", SESSREQ_LOGID(sreq)); - return 1; + lcb_log(LOGARGS(this, INFO), SESSREQ_LOGFMT "Server does not support SASL (no mechanisms supported)", SESSREQ_LOGID(this)); + return MECH_NOT_NEEDED; break; default: - lcb_log(LOGARGS(sreq, INFO), SESSREQ_LOGFMT "cbsasl_client_start returned %d", SESSREQ_LOGID(sreq), saslerr); - sreq->set_error(LCB_EINTERNAL, "Couldn't start SASL client"); - return -1; + lcb_log(LOGARGS(this, INFO), SESSREQ_LOGFMT "cbsasl_client_start returned %d", SESSREQ_LOGID(this), saslerr); + set_error(LCB_EINTERNAL, "Couldn't start SASL client"); + return MECH_UNAVAILABLE; } } /** * Given the specific mechanisms, send the auth packet to the server. */ -static int -send_sasl_auth(mc_SESSREQ *pend, const char *sasl_data, unsigned ndata) +void +SessionRequestImpl::send_auth(const char *sasl_data, unsigned ndata) { - mc_pSESSINFO ctx = pend->sasl; lcb::MemcachedRequest hdr(PROTOCOL_BINARY_CMD_SASL_AUTH); - hdr.sizes(0, ctx->mech.size(), ndata); + hdr.sizes(0, info->mech.size(), ndata); - lcbio_ctx_put(pend->ctx, hdr.data(), hdr.size()); - lcbio_ctx_put(pend->ctx, ctx->mech.c_str(), ctx->mech.size()); - lcbio_ctx_put(pend->ctx, sasl_data, ndata); - lcbio_ctx_rwant(pend->ctx, 24); - return 0; + lcbio_ctx_put(ctx, hdr.data(), hdr.size()); + lcbio_ctx_put(ctx, info->mech.c_str(), info->mech.size()); + lcbio_ctx_put(ctx, sasl_data, ndata); + lcbio_ctx_rwant(ctx, 24); } bool -mc_SESSREQ::send_step(const lcb::MemcachedResponse& packet) +SessionRequestImpl::send_step(const lcb::MemcachedResponse& packet) { cbsasl_error_t saslerr; const char *step_data; unsigned int ndata; - saslerr = cbsasl_client_step(sasl->sasl_client, + saslerr = cbsasl_client_step(sasl_client, packet.body<const char*>(), packet.bodylen(), NULL, &step_data, &ndata); if (saslerr != SASL_CONTINUE) { set_error(LCB_EINTERNAL, "Unable to perform SASL STEP"); return false; } lcb::MemcachedRequest hdr(PROTOCOL_BINARY_CMD_SASL_STEP); - hdr.sizes(0, sasl->mech.size(), ndata); + hdr.sizes(0, info->mech.size(), ndata); lcbio_ctx_put(ctx, hdr.data(), hdr.size()); - lcbio_ctx_put(ctx, sasl->mech.c_str(), sasl->mech.size()); + lcbio_ctx_put(ctx, info->mech.c_str(), info->mech.size()); lcbio_ctx_put(ctx, step_data, ndata); lcbio_ctx_rwant(ctx, 24); return true; } #define LCB_HELLO_DEFL_STRING "libcouchbase/" LCB_VERSION_STRING #define LCB_HELLO_DEFL_LENGTH (sizeof(LCB_HELLO_DEFL_STRING)-1) bool -mc_SESSREQ::send_hello() +SessionRequestImpl::send_hello() { - const lcb_settings *settings = sasl->settings; lcb_U16 features[MEMCACHED_TOTAL_HELLO_FEATURES]; unsigned nfeatures = 0; features[nfeatures++] = PROTOCOL_BINARY_FEATURE_TLS; + features[nfeatures++] = PROTOCOL_BINARY_FEATURE_XATTR; if (settings->tcp_nodelay) { features[nfeatures++] = PROTOCOL_BINARY_FEATURE_TCPNODELAY; } #ifndef LCB_NO_SNAPPY @@ -386,22 +363,22 @@ lcbio_ctx_rwant(ctx, 24); return true; } bool -mc_SESSREQ::read_hello(const lcb::MemcachedResponse& resp) +SessionRequestImpl::read_hello(const lcb::MemcachedResponse& resp) { /* some caps */ const char *cur; const char *payload = resp.body<const char*>(); const char *limit = payload + resp.bodylen(); for (cur = payload; cur < limit; cur += 2) { lcb_U16 tmp; memcpy(&tmp, cur, sizeof(tmp)); tmp = ntohs(tmp); - lcb_log(LOGARGS(this, DEBUG), SESSREQ_LOGFMT "Found feature 0x%x (%s)", SESSREQ_LOGID(this), tmp, protocol_feature_2_text(tmp)); - sasl->server_features.push_back(tmp); + lcb_log(LOGARGS(this, DEBUG), SESSREQ_LOGFMT "Server supports feature: 0x%x (%s)", SESSREQ_LOGID(this), tmp, protocol_feature_2_text(tmp)); + info->server_features.push_back(tmp); } return true; } typedef enum { @@ -413,14 +390,13 @@ /** * It's assumed the server buffers will be reset upon close(), so we must make * sure to _not_ release the ringbuffer if that happens. */ -static void -handle_read(lcbio_CTX *ioctx, unsigned) +void +SessionRequestImpl::handle_read(lcbio_CTX *ioctx) { - mc_pSESSREQ sreq = mc_SESSREQ::get(lcbio_ctx_data(ioctx)); lcb::MemcachedResponse resp; unsigned required; sreq_STATE state = SREQ_S_WAIT; GT_NEXT_PACKET: @@ -431,55 +407,50 @@ } const uint16_t status = resp.status(); switch (resp.opcode()) { case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: { - int mechrc; const char *mechlist_data; unsigned int nmechlist_data; std::string mechs(resp.body<const char*>(), resp.bodylen()); - mechrc = set_chosen_mech(sreq, mechs, &mechlist_data, &nmechlist_data); - if (mechrc == 0) { - if (0 == send_sasl_auth(sreq, mechlist_data, nmechlist_data)) { - state = SREQ_S_WAIT; - } else { - state = SREQ_S_ERROR; - } - - } else if (mechrc < 0) { + MechStatus mechrc = set_chosen_mech(mechs, &mechlist_data, &nmechlist_data); + if (mechrc == MECH_OK) { + send_auth(mechlist_data, nmechlist_data); + state = SREQ_S_WAIT; + } else if (mechrc == MECH_UNAVAILABLE) { state = SREQ_S_ERROR; } else { state = SREQ_S_HELLODONE; } break; } case PROTOCOL_BINARY_CMD_SASL_AUTH: { if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { - sreq->send_hello(); + send_hello(); state = SREQ_S_AUTHDONE; break; } if (status != PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE) { - sreq->set_error(LCB_AUTH_ERROR, "SASL AUTH failed"); + set_error(LCB_AUTH_ERROR, "SASL AUTH failed"); state = SREQ_S_ERROR; break; } - if (sreq->send_step(resp) && sreq->send_hello()) { + if (send_step(resp) && send_hello()) { state = SREQ_S_WAIT; } else { state = SREQ_S_ERROR; } break; } case PROTOCOL_BINARY_CMD_SASL_STEP: { if (status != PROTOCOL_BINARY_RESPONSE_SUCCESS) { - lcb_log(LOGARGS(sreq, WARN), SESSREQ_LOGFMT "SASL auth failed with STATUS=0x%x", SESSREQ_LOGID(sreq), status); - sreq->set_error(LCB_AUTH_ERROR, "SASL Step Failed"); + lcb_log(LOGARGS(this, WARN), SESSREQ_LOGFMT "SASL auth failed with STATUS=0x%x", SESSREQ_LOGID(this), status); + set_error(LCB_AUTH_ERROR, "SASL Step Failed"); state = SREQ_S_ERROR; } else { /* Wait for pipelined HELLO response */ state = SREQ_S_AUTHDONE; } @@ -487,131 +458,126 @@ } case PROTOCOL_BINARY_CMD_HELLO: { state = SREQ_S_HELLODONE; if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { - if (!sreq->read_hello(resp)) { - sreq->set_error(LCB_PROTOCOL_ERROR, "Couldn't parse HELLO"); + if (!read_hello(resp)) { + set_error(LCB_PROTOCOL_ERROR, "Couldn't parse HELLO"); } } else if (status == PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND || status == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED) { - lcb_log(LOGARGS(sreq, DEBUG), SESSREQ_LOGFMT "Server does not support HELLO", SESSREQ_LOGID(sreq)); + lcb_log(LOGARGS(this, DEBUG), SESSREQ_LOGFMT "Server does not support HELLO", SESSREQ_LOGID(this)); /* nothing */ } else { - sreq->set_error(LCB_PROTOCOL_ERROR, "Hello response unexpected"); + set_error(LCB_PROTOCOL_ERROR, "Hello response unexpected"); state = SREQ_S_ERROR; } break; } default: { state = SREQ_S_ERROR; - lcb_log(LOGARGS(sreq, ERROR), SESSREQ_LOGFMT "Received unknown response. OP=0x%x. RC=0x%x", SESSREQ_LOGID(sreq), resp.opcode(), resp.status()); - sreq->set_error(LCB_NOT_SUPPORTED, "Received unknown response"); + lcb_log(LOGARGS(this, ERROR), SESSREQ_LOGFMT "Received unknown response. OP=0x%x. RC=0x%x", SESSREQ_LOGID(this), resp.opcode(), resp.status()); + set_error(LCB_NOT_SUPPORTED, "Received unknown response"); break; } } // We need to release the packet's buffers before actually destroying the // underlying socket and/or buffers! resp.release(ioctx); // Once there is no more any dependencies on the buffers, we can succeed // or fail the request, potentially destroying the underlying connection - if (sreq->has_error()) { - sreq->fail(); + if (has_error()) { + fail(); } else if (state == SREQ_S_ERROR) { - sreq->fail(LCB_ERROR, "FIXME: Error code set without description"); + fail(LCB_ERROR, "FIXME: Error code set without description"); } else if (state == SREQ_S_HELLODONE) { - sreq->success(); + success(); } else { goto GT_NEXT_PACKET; } } static void handle_ioerr(lcbio_CTX *ctx, lcb_error_t err) { - mc_pSESSREQ sreq = mc_SESSREQ::get(lcbio_ctx_data(ctx)); + SessionRequestImpl* sreq = SessionRequestImpl::get(lcbio_ctx_data(ctx)); sreq->fail(err, "IO Error"); } -static void cleanup_negotiated(mc_pSESSINFO ctx) { +static void cleanup_negotiated(SessionInfo* ctx) { delete ctx; } void -mc_SESSREQ::start(lcbio_SOCKET *sock, lcb_settings *settings) { - sasl = new mc_SESSINFO(settings); +SessionRequestImpl::start(lcbio_SOCKET *sock) { + info = new SessionInfo(); lcb_error_t err = lcbio_sslify_if_needed(sock, settings); if (err != LCB_SUCCESS) { set_error(err, "Couldn't initialized SSL on socket"); lcbio_async_signal(timer); return; } lcbio_CTXPROCS procs; - procs.cb_err = handle_ioerr; - procs.cb_read = handle_read; + procs.cb_err = ::handle_ioerr; + procs.cb_read = ::handle_read; ctx = lcbio_ctx_new(sock, this, &procs); ctx->subsys = "sasl"; const lcb_host_t *curhost = lcbio_get_host(sock); - struct lcbio_NAMEINFO nistrs; + lcbio_NAMEINFO nistrs; lcbio_get_nameinfo(sock, &nistrs); - if (!sasl->setup(nistrs, *curhost, *settings->auth)) { + if (!setup(nistrs, *curhost, *settings->auth)) { set_error(LCB_EINTERNAL, "Couldn't start SASL client"); lcbio_async_signal(timer); return; } lcb::MemcachedRequest hdr(PROTOCOL_BINARY_CMD_SASL_LIST_MECHS); lcbio_ctx_put(ctx, hdr.data(), hdr.size()); LCBIO_CTX_RSCHEDULE(ctx, 24); } - -mc_SESSREQ::~mc_SESSREQ() +SessionRequestImpl::~SessionRequestImpl() { - if (sasl) { - delete sasl; + if (info) { + delete info; } if (timer) { lcbio_timer_destroy(timer); } if (ctx) { lcbio_ctx_close(ctx, NULL, NULL); } + if (sasl_client) { + cbsasl_dispose(&sasl_client); + } } -void mc_sessreq_cancel(mc_pSESSREQ sreq) { +void lcb::sessreq_cancel(SessionRequest *sreq) { sreq->cancel(); } -mc_pSESSREQ -mc_sessreq_start(lcbio_SOCKET *sock, lcb_settings *settings, +SessionRequest * +SessionRequest::start(lcbio_SOCKET *sock, lcb_settings_st *settings, uint32_t tmo, lcbio_CONNDONE_cb callback, void *data) { - mc_pSESSREQ sreq = new mc_SESSREQ(callback, data, tmo, sock->io); - sreq->start(sock, settings); + SessionRequestImpl* sreq = new SessionRequestImpl(callback, data, tmo, sock->io, settings); + sreq->start(sock); return sreq; } -mc_pSESSINFO mc_sess_get(lcbio_SOCKET *sock) { - return static_cast<mc_pSESSINFO>( - lcbio_protoctx_get(sock, LCBIO_PROTOCTX_SESSINFO)); +SessionInfo* +SessionInfo::get(lcbio_SOCKET *sock) { + return static_cast<SessionInfo*>(lcbio_protoctx_get(sock, LCBIO_PROTOCTX_SESSINFO)); } -const char *mc_sess_get_saslmech(mc_pSESSINFO info) { - return info->mech.c_str(); -} - -int mc_sess_chkfeature(mc_pSESSINFO info, uint16_t feature) { - for (size_t ii = 0; ii < info->server_features.size(); ++ii) { - if (info->server_features[ii] == feature) { - return 1; - } - } - return 0; +bool +SessionInfo::has_feature(uint16_t feature) const { + return std::find(server_features.begin(), server_features.end(), feature) + != server_features.end(); }