EXPORT_SYM const char * hdfs_namenode_invoke(struct hdfs_namenode *n, struct hdfs_object *rpc, struct hdfs_rpc_response_future *future) { const char *error = NULL; bool nnlocked, needs_kick; int64_t msgno; struct hdfs_heap_buf hbuf = { 0 }; needs_kick = false; ASSERT(future); ASSERT(!future->fu_namenode); ASSERT(rpc); ASSERT(rpc->ob_type == H_RPC_INVOCATION); _lock(&n->nn_lock); nnlocked = true; ASSERT(n->nn_refs >= 1); ASSERT(!n->nn_dead); if (n->nn_sock == -1) { error = "Not connected"; goto out; } if (!n->nn_authed) { error = "Not authenticated"; goto out; } // Take a number msgno = n->nn_msgno; n->nn_msgno++; future->fu_namenode = _namenode_copyref_unlocked(n); _namenode_pending_insert_unlocked(n, msgno, future, _rpc2_slurper_for_rpc(rpc)); if (!n->nn_recver_started) n->nn_recver_started = needs_kick = true; _unlock(&n->nn_lock); nnlocked = false; if (needs_kick) { int rc; rc = pipe(n->nn_recv_sigpipe); ASSERT(rc == 0); rc = pthread_create(&n->nn_recv_thr, NULL, _namenode_recv_worker, n); ASSERT(rc == 0); } // Serialize rpc and transmit _rpc_invocation_set_msgno(rpc, msgno); _rpc_invocation_set_proto(rpc, n->nn_proto); _rpc_invocation_set_clientid(rpc, n->nn_client_id); hdfs_object_serialize(&hbuf, rpc); if (n->nn_sasl_ssf > 0) _sasl_encode_inplace(n->nn_sasl_ctx, &hbuf); _lock(&n->nn_sendlock); error = _write_all(n->nn_sock, hbuf.buf, hbuf.used); _unlock(&n->nn_sendlock); free(hbuf.buf); out: if (nnlocked) _unlock(&n->nn_lock); return error; }
ssize_t _write_str(int fd, const char *str, struct __sourceloc where) { return _write_all(fd, str, strlen(str), where); }
EXPORT_SYM const char * hdfs_namenode_authenticate_full(struct hdfs_namenode *n, const char *username, const char *real_user) { const char *error = NULL; struct hdfs_object *header; struct hdfs_heap_buf hbuf = { 0 }; char *inh = NULL, preamble[12]; size_t preamble_len; _lock(&n->nn_lock); ASSERT(!n->nn_authed); ASSERT(n->nn_sock != -1); memset(preamble, 0, sizeof(preamble)); if (n->nn_proto == HDFS_NN_v1) { sprintf(preamble, "hrpc\x04%c", (n->nn_kerb == HDFS_NO_KERB)? 0x50 : 0x51); preamble_len = 6; header = hdfs_authheader_new_ext(n->nn_proto, username, real_user, HDFS_NO_KERB); } else if (n->nn_proto == HDFS_NN_v2) { /* HDFSv2 has used both version 7 (2.0.0-2.0.2) and 8 (2.0.3+). */ sprintf(preamble, "hrpc%c%c", 8 /* XXX Configurable? */, (n->nn_kerb == HDFS_NO_KERB)? 0x50 : 0x51); /* There is a zero at the end: */ preamble_len = 7; header = hdfs_authheader_new_ext(n->nn_proto, username, real_user, n->nn_kerb); } else if (n->nn_proto == HDFS_NN_v2_2) { memcpy(preamble, "hrpc\x09", 5); preamble[5] = 0; preamble[6] = (n->nn_kerb == HDFS_NO_KERB)? 0 : -33; preamble_len = 7; header = hdfs_authheader_new_ext(n->nn_proto, username, real_user, n->nn_kerb); _authheader_set_clientid(header, n->nn_client_id); } else { ASSERT(false); } // Serialize the connection header object (I am speaking ClientProtocol // and this is my username) hdfs_object_serialize(&hbuf, header); hdfs_object_free(header); if (n->nn_kerb == HDFS_NO_KERB) { // Prefix the header object with the protocol preamble hbuf.buf = realloc(hbuf.buf, hbuf.size + preamble_len); ASSERT(hbuf.buf); memmove(hbuf.buf + preamble_len, hbuf.buf, hbuf.used); memcpy(hbuf.buf, preamble, preamble_len); hbuf.used += preamble_len; hbuf.size += preamble_len; } else { /* * XXX This is probably totally wrong for HDFSv2+. They start * using protobufs at this point to wrap the SASL packets. * * To be fair, it's probably broken for HDFSv1 too :). I need * to find a kerberized HDFS to test against. */ int r; sasl_interact_t *interactions = NULL; const char *out, *mechusing; unsigned outlen; struct iovec iov[3]; uint32_t inlen; const uint32_t SWITCH_TO_SIMPLE_AUTH = (uint32_t)-1; uint8_t in[4], zero[4] = { 0 }; do { r = sasl_client_start(n->nn_sasl_ctx, "GSSAPI", &interactions, &out, &outlen, &mechusing); if (r == SASL_INTERACT) _sasl_interacts(interactions); } while (r == SASL_INTERACT); if (r != SASL_CONTINUE) { error = sasl_errstring(r, NULL, NULL); goto out; } // Send prefix, first auth token _be32enc(in, outlen); iov[0].iov_base = preamble; iov[0].iov_len = preamble_len; iov[1].iov_base = in; iov[1].iov_len = 4; iov[2].iov_base = __DECONST(void *, out); iov[2].iov_len = outlen; error = _writev_all(n->nn_sock, iov, 3); if (error) goto out; do { // read success / error status error = _read_all(n->nn_sock, in, 4); if (error) goto out; if (memcmp(in, zero, 4)) { // error. exception will be next on the wire, // but let's skip it. error = "Got error from server, bailing"; goto out; } // read token len error = _read_all(n->nn_sock, in, 4); if (error) goto out; inlen = _be32dec(in); if (inlen == SWITCH_TO_SIMPLE_AUTH) { if (n->nn_kerb == HDFS_REQUIRE_KERB) { error = "Server tried to drop kerberos but " "client requires it"; goto out; } goto send_header; } // read token if (inh) free(inh); inh = NULL; if (inlen > 0) { inh = malloc(inlen); ASSERT(inh); error = _read_all(n->nn_sock, inh, inlen); if (error) goto out; } out = NULL; outlen = 0; r = sasl_client_step(n->nn_sasl_ctx, inh, inlen, &interactions, &out, &outlen); if (r == SASL_INTERACT) _sasl_interacts(interactions); if (r == SASL_CONTINUE || (r == SASL_OK && out != NULL)) { _be32enc(in, outlen); iov[0].iov_base = in; iov[0].iov_len = 4; iov[1].iov_base = __DECONST(void *, out); iov[1].iov_len = outlen; error = _writev_all(n->nn_sock, iov, 2); if (error) goto out; } } while (r == SASL_INTERACT || r == SASL_CONTINUE); if (r != SASL_OK) { error = sasl_errstring(r, NULL, NULL); goto out; } // sasl connection established n->nn_sasl_ssf = _getssf(n->nn_sasl_ctx); send_header: if (n->nn_sasl_ssf > 0) _sasl_encode_inplace(n->nn_sasl_ctx, &hbuf); } // send auth header error = _write_all(n->nn_sock, hbuf.buf, hbuf.used); n->nn_authed = true; out: if (inh) free(inh); _unlock(&n->nn_lock); free(hbuf.buf); return error; }