/* * 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) { // leak memory 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; }