X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=ndmp-src%2Fndml_conn.c;fp=ndmp-src%2Fndml_conn.c;h=2d4fc06a9284355ee2d16c999c7bb265ba8d63ee;hb=fd48f3e498442f0cbff5f3606c7c403d0566150e;hp=0000000000000000000000000000000000000000;hpb=96f35b20267e8b1a1c846d476f27fcd330e0b018;p=debian%2Famanda diff --git a/ndmp-src/ndml_conn.c b/ndmp-src/ndml_conn.c new file mode 100644 index 0000000..2d4fc06 --- /dev/null +++ b/ndmp-src/ndml_conn.c @@ -0,0 +1,1093 @@ +/* + * Copyright (c) 1998,1999,2000 + * Traakan, Inc., Los Altos, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Project: NDMJOB + * Ident: $Id: $ + * + * Description: + * + */ + + +#include "ndmjob.h" +#include "ndmlib.h" + + +#ifndef NDMOS_OPTION_NO_NDMP4 +#define MAX_PROTOCOL_VERSION NDMP4VER +#else /* !NDMOS_OPTION_NO_NDMP4 */ +#ifndef NDMOS_OPTION_NO_NDMP3 +#define MAX_PROTOCOL_VERSION NDMP3VER +#else /* !NDMOS_OPTION_NO_NDMP3 */ +#ifndef NDMOS_OPTION_NO_NDMP2 +#define MAX_PROTOCOL_VERSION NDMP2VER +#else /* !NDMOS_OPTION_NO_NDMP2 */ +#define MAX_PROTOCOL_VERSION 0 +#endif /* !NDMOS_OPTION_NO_NDM2 */ +#endif /* !NDMOS_OPTION_NO_NDMP3 */ +#endif /* !NDMOS_OPTION_NO_NDMP4 */ + + + +/* + * INITIALIZE AND DESTRUCT + **************************************************************** + * + * Initialize an ndmconn. This pretty much amounts to + * initializing the underlying ndmchan and stuffing + * the function pointers. + */ + +struct ndmconn * +ndmconn_initialize (struct ndmconn *aconn, char *name) +{ + struct ndmconn * conn = aconn; + + if (!conn) { + conn = NDMOS_MACRO_NEW(struct ndmconn); + if (!conn) + return 0; + } + + NDMOS_MACRO_ZEROFILL(conn); + + if (!name) name = "#?"; /* default */ + + ndmchan_initialize (&conn->chan, name); + conn->was_allocated = aconn == 0; + conn->next_sequence = 1; + xdrrec_create (&conn->xdrs, 0, 0, (void*) conn, + (void*)ndmconn_readit, + (void*)ndmconn_writeit); + conn->unexpected = ndmconn_unexpected; + + conn->call = ndmconn_call; + + conn->time_limit = 0; + + return conn; +} + +/* + * Get rid of an ndmconn. + */ +void +ndmconn_destruct (struct ndmconn *conn) +{ + if (conn->chan.fd >= 0) { + close (conn->chan.fd); + conn->chan.fd = -1; + } + + xdr_destroy (&conn->xdrs); + + if (conn->was_allocated) { + NDMOS_API_FREE (conn); + conn = 0; + } +} + +/* + * ESTABLISH CONNECTION + **************************************************************** + * + * The following four routines establish the TCP/IP connection + * between agents. + * + * ndmconn_connect_agent() + * make a connection per an ndmagent, uses ..._host_port() + * ndmconn_connect_host_port () + * make a connection per a hostname and port, uses ..._sockaddr_in() + * ndmconn_connect_sockaddr_in() + * make a connection per sockaddr_in, performs NDMP_CONNECT_ + * sequences, but no authentication + * ndmconn_accept() + * make a connection (receive it really) from a file descriptor + * already accept()ed. + */ + +int +ndmconn_connect_agent (struct ndmconn *conn, struct ndmagent *agent) +{ + if (agent->conn_type == NDMCONN_TYPE_RESIDENT) { + conn->conn_type = NDMCONN_TYPE_RESIDENT; + conn->protocol_version = agent->protocol_version; + if (conn->protocol_version == 0) { + /* Let's negotiate......MAX */ + conn->protocol_version = MAX_PROTOCOL_VERSION; + } + ndmchan_start_resident (&conn->chan); + return 0; + } + + if (agent->port == 0) agent->port = NDMPPORT; + + return ndmconn_connect_host_port (conn, + agent->host, agent->port, agent->protocol_version); +} + + +int +ndmconn_connect_host_port (struct ndmconn *conn, + char * hostname, int port, unsigned want_protocol_version) +{ + struct sockaddr_in sin; + char * err = "???"; + + if (conn->chan.fd >= 0) { + err = "already-connected"; + return ndmconn_set_err_msg (conn, err); + } + + if (ndmhost_lookup (hostname, &sin) != 0) { + err = "bad-host-name"; + return ndmconn_set_err_msg (conn, err); + } + + if (port == 0) port = NDMPPORT; + + sin.sin_port = htons(port); + + return ndmconn_connect_sockaddr_in (conn, &sin, want_protocol_version); +} + +int +ndmconn_connect_sockaddr_in (struct ndmconn *conn, + struct sockaddr_in *sin, unsigned want_protocol_version) +{ + int fd = -1; + int rc; + char * err = "???"; + unsigned max_protocol_version = MAX_PROTOCOL_VERSION; + + if (conn->chan.fd >= 0) { + err = "already-connected"; + return ndmconn_set_err_msg (conn, err); + } + + fd = socket (AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + err = malloc(1024); + snprintf(err, 1023, "open a socket failed: %s", strerror(errno)); + goto error_out; + } + + /* reserved port? */ + if (connect (fd, (struct sockaddr *)sin, sizeof *sin) < 0) { + err = malloc(1024); + snprintf(err, 1023, "connect failed: %s", strerror(errno)); + goto error_out; + } + + ndmchan_start_readchk (&conn->chan, fd); + conn->conn_type = NDMCONN_TYPE_REMOTE; + + /* + * Await the NDMP_NOTIFY_CONNECTED request (no reply) + * Don't get confused that this client-side is awaiting + * a "request" from the server. + */ + NDMC_WITH_NO_REPLY(ndmp0_notify_connected,0) + rc = ndmconn_recv_nmb(conn, &xa->request); + if (rc != 0) { + err = "recv-notify-connected"; + goto error_out; + } + if (xa->request.header.message_type != NDMP0_MESSAGE_REQUEST + || xa->request.header.message != NDMP0_NOTIFY_CONNECTED) { + err = "msg-not-notify-connected"; + goto error_out; + } + + if (request->reason != NDMP0_CONNECTED) { + err = "notify-connected-not-connected"; + goto error_out; + } + + if (max_protocol_version > request->protocol_version) { + max_protocol_version = request->protocol_version; + } + NDMC_ENDWITH + + if (want_protocol_version == 0) { + want_protocol_version = max_protocol_version; + } else if (want_protocol_version > max_protocol_version) { + err = "connect-want/max-version-mismatch"; + goto error_out; + } + + /* + * Send the OPEN request + */ + NDMC_WITH(ndmp0_connect_open,0) + request->protocol_version = want_protocol_version; + rc = NDMC_CALL(conn); + if (rc) { + err = "connect-open-failed"; + goto error_out; + } + NDMC_ENDWITH + + /* GOOD! */ + + conn->protocol_version = want_protocol_version; + + return 0; + + error_out: + if (fd >= 0) { + close (fd); + fd = -1; + } + conn->chan.fd = -1; + conn->chan.mode = NDMCHAN_MODE_IDLE; + conn->conn_type = NDMCONN_TYPE_NONE; + + return ndmconn_set_err_msg (conn, err); +} + +int +ndmconn_try_open (struct ndmconn *conn, unsigned protocol_version) +{ + int rc; + + /* + * Send the OPEN request + */ + NDMC_WITH(ndmp0_connect_open,0) + request->protocol_version = protocol_version; + rc = NDMC_CALL(conn); + if (rc) { + ndmconn_set_err_msg (conn, "connect-open-failed"); + } + NDMC_ENDWITH + + return rc; +} + +int +ndmconn_accept (struct ndmconn *conn, int sock) +{ + char * err = "???"; + + if (conn->chan.fd >= 0) { + err = "already-connected"; + return ndmconn_set_err_msg (conn, err); + } + + ndmchan_start_readchk (&conn->chan, sock); + conn->conn_type = NDMCONN_TYPE_REMOTE; + + /* + * Send the NDMP_NOTIFY_CONNECTED message, no reply + * The connect()er is waiting for it. + */ + NDMC_WITH_NO_REPLY(ndmp0_notify_connected,0) + request->reason = NDMP0_CONNECTED; + request->protocol_version = MAX_PROTOCOL_VERSION; + request->text_reason = "Hello"; + NDMC_SEND(conn); + NDMC_ENDWITH + + /* assume connection is running in offered protocol_version */ + conn->protocol_version = MAX_PROTOCOL_VERSION; + + return 0; +} + + + + +/* + * TERMINATE CONNECTION + **************************************************************** + * + * These two routines are about terminating a connection. + * They are incomplete. + */ + +/* hangup */ +int +ndmconn_abort (struct ndmconn *conn) +{ + return 0; +} + +/* orderly close */ +int +ndmconn_close (struct ndmconn *conn) +{ + return 0; +} + + +/* + * Return the underlying fd of the ndmconn. + * This is no longer used since the ndmchan stuff was done. + */ + +int +ndmconn_fileno (struct ndmconn *conn) +{ + return conn->chan.fd; +} + + + + + +/* + * AUTHENTICATION + * + * The following three routines do the NDMP_CONNECT_AUTH sequences. + */ + +int +ndmconn_auth_agent (struct ndmconn *conn, struct ndmagent *agent) +{ + int rc; + + if (conn->conn_type == NDMCONN_TYPE_RESIDENT) + return 0; + + switch (agent->auth_type) { + case 'n': /* NDMP_AUTH_NONE */ + rc = ndmconn_auth_none (conn); + break; + + case 't': /* NDMP_AUTH_TEXT */ + rc = ndmconn_auth_text (conn, agent->account, agent->password); + break; + + case 'm': /* NDMP_AUTH_MD5 */ + rc = ndmconn_auth_md5 (conn, agent->account, agent->password); + break; + + case 'v': /* void (don't auth) */ + rc = 0; + break; + + default: + ndmconn_set_err_msg (conn, "connect-auth-unknown"); + rc = -1; + break; + } + + return rc; +} + +int +ndmconn_auth_none (struct ndmconn *conn) +{ + int rc; + + switch (conn->protocol_version) { + default: + ndmconn_set_err_msg (conn, "connect-auth-none-vers-botch"); + return -1; + +#ifndef NDMOS_OPTION_NO_NDMP2 + case NDMP2VER: + NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER) + request->auth_data.auth_type = NDMP2_AUTH_NONE; + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP2 */ + +#ifndef NDMOS_OPTION_NO_NDMP3 + case NDMP3VER: + NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER) + request->auth_data.auth_type = NDMP3_AUTH_NONE; + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP3 */ + +#ifndef NDMOS_OPTION_NO_NDMP4 + case NDMP4VER: + NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER) + request->auth_data.auth_type = NDMP4_AUTH_NONE; + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP4 */ + } + + if (rc) { + ndmconn_set_err_msg (conn, "connect-auth-none-failed"); + return -1; + } + + return 0; +} + +int +ndmconn_auth_text (struct ndmconn *conn, char *id, char *pw) +{ + int rc; + + switch (conn->protocol_version) { + default: + ndmconn_set_err_msg (conn, "connect-auth-text-vers-botch"); + return -1; + +#ifndef NDMOS_OPTION_NO_NDMP2 + case NDMP2VER: + NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER) + struct ndmp2_auth_text *at; + + request->auth_data.auth_type = NDMP2_AUTH_TEXT; + at = &request->auth_data.ndmp2_auth_data_u.auth_text; + at->auth_id = id; + at->auth_password = pw; + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP2 */ + +#ifndef NDMOS_OPTION_NO_NDMP3 + case NDMP3VER: + NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER) + struct ndmp3_auth_text *at; + + request->auth_data.auth_type = NDMP3_AUTH_TEXT; + at = &request->auth_data.ndmp3_auth_data_u.auth_text; + at->auth_id = id; + at->auth_password = pw; + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP3 */ + +#ifndef NDMOS_OPTION_NO_NDMP4 + case NDMP4VER: + NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER) + struct ndmp4_auth_text *at; + + request->auth_data.auth_type = NDMP4_AUTH_TEXT; + at = &request->auth_data.ndmp4_auth_data_u.auth_text; + at->auth_id = id; + at->auth_password = pw; + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP4 */ + } + + if (rc) { + ndmconn_set_err_msg (conn, "connect-auth-text-failed"); + return -1; + } + + return 0; +} + +int +ndmconn_auth_md5 (struct ndmconn *conn, char *id, char *pw) +{ + int rc; + char challenge[NDMP_MD5_CHALLENGE_LENGTH]; + char digest[NDMP_MD5_DIGEST_LENGTH]; + + switch (conn->protocol_version) { + default: + ndmconn_set_err_msg (conn, "connect-auth-md5-vers-botch"); + return -1; + +#ifndef NDMOS_OPTION_NO_NDMP2 + case NDMP2VER: + NDMC_WITH(ndmp2_config_get_auth_attr, NDMP2VER) + request->auth_type = NDMP2_AUTH_MD5; + rc = NDMC_CALL(conn); + if (rc == 0) { + if (reply->server_attr.auth_type != NDMP2_AUTH_MD5) { + ndmconn_set_err_msg (conn, + "connect-auth-md5-attr-type-botch"); + return -1; + } + NDMOS_API_BCOPY ( + reply->server_attr.ndmp2_auth_attr_u.challenge, + challenge, sizeof challenge); + } + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP2 */ + +#ifndef NDMOS_OPTION_NO_NDMP3 + case NDMP3VER: + NDMC_WITH(ndmp3_config_get_auth_attr, NDMP3VER) + request->auth_type = NDMP3_AUTH_MD5; + rc = NDMC_CALL(conn); + if (rc == 0) { + if (reply->server_attr.auth_type != NDMP3_AUTH_MD5) { + ndmconn_set_err_msg (conn, + "connect-auth-md5-attr-type-botch"); + return -1; + } + NDMOS_API_BCOPY ( + reply->server_attr.ndmp3_auth_attr_u.challenge, + challenge, sizeof challenge); + } + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP3 */ + +#ifndef NDMOS_OPTION_NO_NDMP4 + case NDMP4VER: + NDMC_WITH(ndmp4_config_get_auth_attr, NDMP4VER) + request->auth_type = NDMP4_AUTH_MD5; + rc = NDMC_CALL(conn); + if (rc == 0) { + if (reply->server_attr.auth_type != NDMP4_AUTH_MD5) { + ndmconn_set_err_msg (conn, + "connect-auth-md5-attr-type-botch"); + return -1; + } + NDMOS_API_BCOPY ( + reply->server_attr.ndmp4_auth_attr_u.challenge, + challenge, sizeof challenge); + } + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP4 */ + } + + if (rc) { + ndmconn_set_err_msg (conn, "connect-auth-md5-attr-failed"); + return -1; + } + + ndmmd5_digest (challenge, pw, digest); + + switch (conn->protocol_version) { + default: + ndmconn_set_err_msg (conn, "connect-auth-text-vers-botch"); + return -1; + +#ifndef NDMOS_OPTION_NO_NDMP2 + case NDMP2VER: + NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER) + struct ndmp2_auth_md5 *am; + + request->auth_data.auth_type = NDMP2_AUTH_MD5; + am = &request->auth_data.ndmp2_auth_data_u.auth_md5; + am->auth_id = id; + NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest); + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP2 */ + +#ifndef NDMOS_OPTION_NO_NDMP3 + case NDMP3VER: + NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER) + struct ndmp3_auth_md5 *am; + + request->auth_data.auth_type = NDMP3_AUTH_MD5; + am = &request->auth_data.ndmp3_auth_data_u.auth_md5; + am->auth_id = id; + NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest); + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP3 */ + +#ifndef NDMOS_OPTION_NO_NDMP4 + case NDMP4VER: + NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER) + struct ndmp4_auth_md5 *am; + + request->auth_data.auth_type = NDMP4_AUTH_MD5; + am = &request->auth_data.ndmp4_auth_data_u.auth_md5; + am->auth_id = id; + NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest); + rc = NDMC_CALL(conn); + NDMC_ENDWITH + break; +#endif /* !NDMOS_OPTION_NO_NDMP4 */ + } + + if (rc) { + ndmconn_set_err_msg (conn, "connect-auth-md5-failed"); + return -1; + } + + return 0; +} + + + + +/* + * CALL (REQUEST/REPLY), SEND, and RECEIVE + **************************************************************** + */ + +int +ndmconn_call (struct ndmconn *conn, struct ndmp_xa_buf *xa) +{ + unsigned protocol_version = conn->protocol_version; + unsigned msg = xa->request.header.message; + int rc; + struct ndmp_xdr_message_table * xmte; + + conn->last_message = msg; + conn->last_call_status = NDMCONN_CALL_STATUS_BOTCH; + conn->last_header_error = -1; /* invalid */ + conn->last_reply_error = -1; /* invalid */ + + if (protocol_version != xa->request.protocol_version) { + ndmconn_set_err_msg (conn, "protocol-version-mismatch"); + return NDMCONN_CALL_STATUS_BOTCH; + } + + xmte = ndmp_xmt_lookup (protocol_version, msg); + if (!xmte) { + ndmconn_set_err_msg (conn, "no-xdr-found"); + return NDMCONN_CALL_STATUS_BOTCH; + } + + xa->request.header.message_type = NDMP0_MESSAGE_REQUEST; + + if (!xmte->xdr_reply) { + /* no reply expected, just a send (eg NOTIFY) */ + return ndmconn_send_nmb (conn, &xa->request); + } + + rc = ndmconn_exchange_nmb (conn, &xa->request, &xa->reply); + if (rc) { + ndmconn_set_err_msg (conn, "exchange-failed"); + return NDMCONN_CALL_STATUS_BOTCH; + } + + if (xa->reply.header.message != msg) { + ndmconn_set_err_msg (conn, "msg-mismatch"); + return NDMCONN_CALL_STATUS_BOTCH; + } + + /* TODO: this should be converted ndmp_xto9_error(....) */ + conn->last_header_error = xa->reply.header.error; + + if (xa->reply.header.error) { + conn->last_call_status = NDMCONN_CALL_STATUS_HDR_ERROR; + ndmconn_set_err_msg (conn, "reply-error-hdr"); + return NDMCONN_CALL_STATUS_HDR_ERROR; + } + + conn->last_reply_error = ndmnmb_get_reply_error (&xa->reply); + + if (conn->last_reply_error != NDMP9_NO_ERR) { + conn->last_call_status = NDMCONN_CALL_STATUS_REPLY_ERROR; + ndmconn_set_err_msg (conn, "reply-error"); + return NDMCONN_CALL_STATUS_REPLY_ERROR; + } + + return NDMCONN_CALL_STATUS_OK; +} + +int +ndmconn_exchange_nmb (struct ndmconn *conn, + struct ndmp_msg_buf *request_nmb, + struct ndmp_msg_buf *reply_nmb) +{ + int rc; + + if ((rc = ndmconn_send_nmb (conn, request_nmb)) != 0) + return rc; + conn->received_time = 0; + conn->sent_time = time(0); + + for (;;) { + if ((rc = ndmconn_recv_nmb (conn, reply_nmb)) != 0) + return rc; + + if (reply_nmb->header.message_type == NDMP0_MESSAGE_REPLY + && reply_nmb->header.reply_sequence + == request_nmb->header.sequence) { + conn->received_time = time(0); + return 0; + } + + (*conn->unexpected)(conn, reply_nmb); + } +} + +int +ndmconn_send_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb) +{ + return ndmconn_xdr_nmb (conn, nmb, XDR_ENCODE); +} + +int +ndmconn_recv_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb) +{ + NDMOS_MACRO_ZEROFILL (nmb); + nmb->protocol_version = conn->protocol_version; + + return ndmconn_xdr_nmb (conn, nmb, XDR_DECODE); +} + +void +ndmconn_free_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb) +{ + ndmnmb_free (nmb); +} + + +int +ndmconn_xdr_nmb (struct ndmconn *conn, + struct ndmp_msg_buf *nmb, + enum xdr_op x_op) +{ + xdrproc_t xdr_body = 0; + + assert (conn->conn_type == NDMCONN_TYPE_REMOTE); + + if (conn->chan.fd < 0) { + return ndmconn_set_err_msg (conn, "not-open"); + } + + conn->xdrs.x_op = x_op; + + if (x_op == XDR_ENCODE) { + xdr_body = ndmnmb_find_xdrproc (nmb); + + if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) { + return ndmconn_set_err_msg (conn, "unknown-body"); + } + nmb->header.sequence = conn->next_sequence++; + nmb->header.time_stamp = time(0); + ndmconn_snoop_nmb (conn, nmb, "Send"); + } + if (x_op == XDR_DECODE) { + if (!xdrrec_skiprecord (&conn->xdrs)) { + return ndmconn_set_err_msg (conn, "xdr-get-next"); + } + } + + if (!xdr_ndmp0_header (&conn->xdrs, &nmb->header)) { + ndmconn_abort (conn); + if (x_op == XDR_DECODE + && conn->chan.eof && !conn->chan.error) { + return ndmconn_set_err_msg (conn, "EOF"); + } else { + return ndmconn_set_err_msg (conn, "xdr-hdr"); + } + } + + if (x_op == XDR_DECODE) { + xdr_body = ndmnmb_find_xdrproc (nmb); + + if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) { + return ndmconn_set_err_msg (conn, "unknown-body"); + } + } + if (nmb->header.error == NDMP0_NO_ERR) { + if (!(*xdr_body) (&conn->xdrs, &nmb->body)) { + ndmconn_abort (conn); + return ndmconn_set_err_msg (conn, "xdr-body"); + } + } + + if (x_op == XDR_ENCODE) { + if (!xdrrec_endofrecord(&conn->xdrs, 1)) { + ndmconn_abort (conn); + return ndmconn_set_err_msg (conn, "xdr-send"); + } + } + if (x_op == XDR_DECODE) { + ndmconn_snoop_nmb (conn, nmb, "Recv"); + } + + return 0; +} + + + + +/* + * XDR READ/WRITE CALLBACKS + **************************************************************** + * + * ndmconn_readit() and ndmconn_writeit() are the XDR callbacks + * used by xdrrec_create(). They are fundamentally wrappers + * around read() and write(), and have very similar parameters. + * See the xdr(3) manual page (or try "man xdrrec_create"). + * + * ndmconn_readit() tracks the XDR record marks, and never + * reads across a record boundary. This keeps select() an + * indicator of when there is a (single) request pending. + * Otherwise, we have to check buffers internal to XDR + * as well as the file descriptor (via select) to determine + * if a request is pending. + */ + +int +ndmconn_readit (void *a_conn, char *buf, int len) +{ + struct ndmconn *conn = (struct ndmconn *)a_conn; + int rc, i, c; + + /* could impose timeout here */ + if (conn->chan.fd < 0 || conn->chan.eof) + return -1; + + ndmconn_snoop (conn, 8, + "frag_resid=%d fhb_off=%d", conn->frag_resid, conn->fhb_off); + + if (conn->frag_resid == 0) { + i = 0; + while (i < 4) { + c = 4 - i; + + rc = ndmconn_sys_read (conn, (void *)(conn->frag_hdr_buf+i), c); + if (rc <= 0) { + return rc; + } + i += rc; + } + conn->frag_resid = conn->frag_hdr_buf[0] << 24; + conn->frag_resid |= conn->frag_hdr_buf[1] << 16; + conn->frag_resid |= conn->frag_hdr_buf[2] << 8; + conn->frag_resid |= conn->frag_hdr_buf[3]; + conn->frag_resid &= 0xFFFFFF; + conn->fhb_off = 0; + } + if (conn->fhb_off < 4) { + i = 0; + while (conn->fhb_off < 4 && len > 0) { + buf[i++] = conn->frag_hdr_buf[conn->fhb_off++]; + len--; + } + return i; + } + + if ((unsigned int)len > conn->frag_resid) + len = (unsigned int)conn->frag_resid; + + rc = ndmconn_sys_read (conn, buf, len); + + if (rc > 0) { + conn->frag_resid -= rc; + } + + return rc; +} + +int +ndmconn_writeit (void *a_conn, char *buf, int len) +{ + struct ndmconn *conn = (struct ndmconn *)a_conn; + + /* could impose timeout here */ + if (conn->chan.fd < 0) + return -1; + + return ndmconn_sys_write (conn, buf, len); +} + +/* + * ndmconn_sys_read() and ndmconn_sys_write() are simply + * wrappers around read() and write(). They implement + * the low-level snooping. + */ + +int +ndmconn_sys_read (struct ndmconn *conn, char *buf, unsigned len) +{ + int rc; + + ndmconn_snoop (conn, 9, "reading %d ...", len); + + rc = read (conn->chan.fd, buf, len); + + ndmconn_snoop (conn, 8, "read=%d len=%d", rc, len); + ndmconn_hex_dump (conn, buf, rc); + + if (rc <= 0) { + conn->chan.eof = 1; + if (rc < 0) + conn->chan.error = 1; + } + + return rc; +} + +int +ndmconn_sys_write (struct ndmconn *conn, char *buf, unsigned len) +{ + int rc; + + ndmconn_snoop (conn, 9, "writing %d ...", len); + ndmconn_hex_dump (conn, buf, len); + + rc = write (conn->chan.fd, buf, len); + + ndmconn_snoop (conn, 8, "write=%d len=%d", rc, len); + + if (rc !=(int)len) { + conn->chan.eof = 1; + conn->chan.error = 1; + } + + return rc; +} + + + + +/* + * UNEXPECTED + **************************************************************** + * + * The default unexpected() handler for a connection. It is + * called when ndmconn_exchange_nmb() receives something + * other than the reply for which it is waiting. + * This default routine silently dumps the message. + */ + +void +ndmconn_unexpected (struct ndmconn *conn, struct ndmp_msg_buf *nmb) +{ + xdrproc_t xdr_body = ndmnmb_find_xdrproc (nmb); + + if (xdr_body) { + xdr_free (xdr_body, (void*) &nmb->body); + } +} + + + + +/* + * SNOOP + **************************************************************** + * + * The ndmconn snoop stuff. The cool part. This pretty prints + * NDMP messages as they go flying by this end-point. + */ + +int +ndmconn_set_snoop (struct ndmconn *conn, struct ndmlog *log, int level) +{ + conn->snoop_log = log; + conn->snoop_level = level; + return 0; +} + +void +ndmconn_clear_snoop (struct ndmconn *conn) +{ + conn->snoop_log = 0; + conn->snoop_level = 0; +} + +void +ndmconn_snoop_nmb (struct ndmconn *conn, + struct ndmp_msg_buf *nmb, + char *whence) +{ + if (!conn->snoop_log) { + return; + } + + ndmnmb_snoop (conn->snoop_log, conn->chan.name, conn->snoop_level, + nmb, whence); +} + +void +ndmconn_snoop (struct ndmconn *conn, int level, char *fmt, ...) +{ + va_list ap; + + if (conn->snoop_log && conn->snoop_level >= level) { + va_start (ap, fmt); + ndmlogfv (conn->snoop_log, conn->chan.name, level, fmt, ap); + va_end (ap); + } +} + +/* used by ndmconn_sys_read() and ndmconn_sys_write() to show low-level */ +void +ndmconn_hex_dump (struct ndmconn *conn, char *buf, unsigned len) +{ + struct ndmlog * log = conn->snoop_log; + char * tag = conn->chan.name; + char linebuf[16*3+3]; + char * p = linebuf; + int b; + unsigned i; + + if (log && conn->snoop_level > 8) { + for (i = 0; i < len; i++) { + b = buf[i] & 0xFF; + sprintf (p, " %02x", b); + while (*p) p++; + if ((i&0xF) == 0xF) { + ndmlogf (log,tag,9,"%s",linebuf+1); + p = linebuf; + } + } + if (p > linebuf) { + ndmlogf (log,tag,9,"%s",linebuf+1); + } + } +} + + + + +/* + * ERRORS + **************************************************************** + * + * Possible errors for ndmconn are not enumerated. + * Instead, errors are indicated by a -1 return, and + * a simple string error message is available for details. + * Appologies for the english-centric design, but it + * is quick and easy, and better than using printf(). + */ + +int +ndmconn_set_err_msg (struct ndmconn *conn, char *err_msg) +{ + conn->last_err_msg = err_msg; + ndmconn_snoop (conn, 4, "ERR=%s", err_msg); + return -1; +} + +char * +ndmconn_get_err_msg (struct ndmconn *conn) +{ + if (!conn->last_err_msg) + return "-no-error-"; + else + return conn->last_err_msg; +}