2 * Copyright (c) 1998,1999,2000
3 * Traakan, Inc., Los Altos, CA
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #ifndef NDMOS_OPTION_NO_NDMP4
43 #define MAX_PROTOCOL_VERSION NDMP4VER
44 #else /* !NDMOS_OPTION_NO_NDMP4 */
45 #ifndef NDMOS_OPTION_NO_NDMP3
46 #define MAX_PROTOCOL_VERSION NDMP3VER
47 #else /* !NDMOS_OPTION_NO_NDMP3 */
48 #ifndef NDMOS_OPTION_NO_NDMP2
49 #define MAX_PROTOCOL_VERSION NDMP2VER
50 #else /* !NDMOS_OPTION_NO_NDMP2 */
51 #define MAX_PROTOCOL_VERSION 0
52 #endif /* !NDMOS_OPTION_NO_NDM2 */
53 #endif /* !NDMOS_OPTION_NO_NDMP3 */
54 #endif /* !NDMOS_OPTION_NO_NDMP4 */
59 * INITIALIZE AND DESTRUCT
60 ****************************************************************
62 * Initialize an ndmconn. This pretty much amounts to
63 * initializing the underlying ndmchan and stuffing
64 * the function pointers.
68 ndmconn_initialize (struct ndmconn *aconn, char *name)
70 struct ndmconn * conn = aconn;
73 conn = NDMOS_MACRO_NEW(struct ndmconn);
78 NDMOS_MACRO_ZEROFILL(conn);
80 if (!name) name = "#?"; /* default */
82 ndmchan_initialize (&conn->chan, name);
83 conn->was_allocated = aconn == 0;
84 conn->next_sequence = 1;
85 xdrrec_create (&conn->xdrs, 0, 0, (void*) conn,
86 (void*)ndmconn_readit,
87 (void*)ndmconn_writeit);
88 conn->unexpected = ndmconn_unexpected;
90 conn->call = ndmconn_call;
98 * Get rid of an ndmconn.
101 ndmconn_destruct (struct ndmconn *conn)
103 if (conn->chan.fd >= 0) {
104 close (conn->chan.fd);
108 xdr_destroy (&conn->xdrs);
110 if (conn->was_allocated) {
111 NDMOS_API_FREE (conn);
117 * ESTABLISH CONNECTION
118 ****************************************************************
120 * The following four routines establish the TCP/IP connection
123 * ndmconn_connect_agent()
124 * make a connection per an ndmagent, uses ..._host_port()
125 * ndmconn_connect_host_port ()
126 * make a connection per a hostname and port, uses ..._sockaddr_in()
127 * ndmconn_connect_sockaddr_in()
128 * make a connection per sockaddr_in, performs NDMP_CONNECT_
129 * sequences, but no authentication
131 * make a connection (receive it really) from a file descriptor
132 * already accept()ed.
136 ndmconn_connect_agent (struct ndmconn *conn, struct ndmagent *agent)
138 if (agent->conn_type == NDMCONN_TYPE_RESIDENT) {
139 conn->conn_type = NDMCONN_TYPE_RESIDENT;
140 conn->protocol_version = agent->protocol_version;
141 if (conn->protocol_version == 0) {
142 /* Let's negotiate......MAX */
143 conn->protocol_version = MAX_PROTOCOL_VERSION;
145 ndmchan_start_resident (&conn->chan);
149 if (agent->port == 0) agent->port = NDMPPORT;
151 return ndmconn_connect_host_port (conn,
152 agent->host, agent->port, agent->protocol_version);
157 ndmconn_connect_host_port (struct ndmconn *conn,
158 char * hostname, int port, unsigned want_protocol_version)
160 struct sockaddr_in sin;
163 if (conn->chan.fd >= 0) {
164 err = "already-connected";
165 return ndmconn_set_err_msg (conn, err);
168 if (ndmhost_lookup (hostname, &sin) != 0) {
169 err = "bad-host-name";
170 return ndmconn_set_err_msg (conn, err);
173 if (port == 0) port = NDMPPORT;
175 sin.sin_port = htons(port);
177 return ndmconn_connect_sockaddr_in (conn, &sin, want_protocol_version);
181 ndmconn_connect_sockaddr_in (struct ndmconn *conn,
182 struct sockaddr_in *sin, unsigned want_protocol_version)
187 unsigned max_protocol_version = MAX_PROTOCOL_VERSION;
189 if (conn->chan.fd >= 0) {
190 err = "already-connected";
191 return ndmconn_set_err_msg (conn, err);
194 fd = socket (AF_INET, SOCK_STREAM, 0);
197 snprintf(err, 1023, "open a socket failed: %s", strerror(errno));
202 if (connect (fd, (struct sockaddr *)sin, sizeof *sin) < 0) {
204 snprintf(err, 1023, "connect failed: %s", strerror(errno));
208 ndmchan_start_readchk (&conn->chan, fd);
209 conn->conn_type = NDMCONN_TYPE_REMOTE;
212 * Await the NDMP_NOTIFY_CONNECTED request (no reply)
213 * Don't get confused that this client-side is awaiting
214 * a "request" from the server.
216 NDMC_WITH_NO_REPLY(ndmp0_notify_connected,0)
217 rc = ndmconn_recv_nmb(conn, &xa->request);
219 err = "recv-notify-connected";
222 if (xa->request.header.message_type != NDMP0_MESSAGE_REQUEST
223 || xa->request.header.message != NDMP0_NOTIFY_CONNECTED) {
224 err = "msg-not-notify-connected";
228 if (request->reason != NDMP0_CONNECTED) {
229 err = "notify-connected-not-connected";
233 if (max_protocol_version > request->protocol_version) {
234 max_protocol_version = request->protocol_version;
238 if (want_protocol_version == 0) {
239 want_protocol_version = max_protocol_version;
240 } else if (want_protocol_version > max_protocol_version) {
241 err = "connect-want/max-version-mismatch";
246 * Send the OPEN request
248 NDMC_WITH(ndmp0_connect_open,0)
249 request->protocol_version = want_protocol_version;
250 rc = NDMC_CALL(conn);
252 err = "connect-open-failed";
259 conn->protocol_version = want_protocol_version;
269 conn->chan.mode = NDMCHAN_MODE_IDLE;
270 conn->conn_type = NDMCONN_TYPE_NONE;
272 return ndmconn_set_err_msg (conn, err);
276 ndmconn_try_open (struct ndmconn *conn, unsigned protocol_version)
281 * Send the OPEN request
283 NDMC_WITH(ndmp0_connect_open,0)
284 request->protocol_version = protocol_version;
285 rc = NDMC_CALL(conn);
287 ndmconn_set_err_msg (conn, "connect-open-failed");
295 ndmconn_accept (struct ndmconn *conn, int sock)
299 if (conn->chan.fd >= 0) {
300 err = "already-connected";
301 return ndmconn_set_err_msg (conn, err);
304 ndmchan_start_readchk (&conn->chan, sock);
305 conn->conn_type = NDMCONN_TYPE_REMOTE;
308 * Send the NDMP_NOTIFY_CONNECTED message, no reply
309 * The connect()er is waiting for it.
311 NDMC_WITH_NO_REPLY(ndmp0_notify_connected,0)
312 request->reason = NDMP0_CONNECTED;
313 request->protocol_version = MAX_PROTOCOL_VERSION;
314 request->text_reason = "Hello";
318 /* assume connection is running in offered protocol_version */
319 conn->protocol_version = MAX_PROTOCOL_VERSION;
328 * TERMINATE CONNECTION
329 ****************************************************************
331 * These two routines are about terminating a connection.
332 * They are incomplete.
337 ndmconn_abort (struct ndmconn *conn)
344 ndmconn_close (struct ndmconn *conn)
351 * Return the underlying fd of the ndmconn.
352 * This is no longer used since the ndmchan stuff was done.
356 ndmconn_fileno (struct ndmconn *conn)
358 return conn->chan.fd;
368 * The following three routines do the NDMP_CONNECT_AUTH sequences.
372 ndmconn_auth_agent (struct ndmconn *conn, struct ndmagent *agent)
376 if (conn->conn_type == NDMCONN_TYPE_RESIDENT)
379 switch (agent->auth_type) {
380 case 'n': /* NDMP_AUTH_NONE */
381 rc = ndmconn_auth_none (conn);
384 case 't': /* NDMP_AUTH_TEXT */
385 rc = ndmconn_auth_text (conn, agent->account, agent->password);
388 case 'm': /* NDMP_AUTH_MD5 */
389 rc = ndmconn_auth_md5 (conn, agent->account, agent->password);
392 case 'v': /* void (don't auth) */
397 ndmconn_set_err_msg (conn, "connect-auth-unknown");
406 ndmconn_auth_none (struct ndmconn *conn)
410 switch (conn->protocol_version) {
412 ndmconn_set_err_msg (conn, "connect-auth-none-vers-botch");
415 #ifndef NDMOS_OPTION_NO_NDMP2
417 NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
418 request->auth_data.auth_type = NDMP2_AUTH_NONE;
419 rc = NDMC_CALL(conn);
422 #endif /* !NDMOS_OPTION_NO_NDMP2 */
424 #ifndef NDMOS_OPTION_NO_NDMP3
426 NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
427 request->auth_data.auth_type = NDMP3_AUTH_NONE;
428 rc = NDMC_CALL(conn);
431 #endif /* !NDMOS_OPTION_NO_NDMP3 */
433 #ifndef NDMOS_OPTION_NO_NDMP4
435 NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
436 request->auth_data.auth_type = NDMP4_AUTH_NONE;
437 rc = NDMC_CALL(conn);
440 #endif /* !NDMOS_OPTION_NO_NDMP4 */
444 ndmconn_set_err_msg (conn, "connect-auth-none-failed");
452 ndmconn_auth_text (struct ndmconn *conn, char *id, char *pw)
456 switch (conn->protocol_version) {
458 ndmconn_set_err_msg (conn, "connect-auth-text-vers-botch");
461 #ifndef NDMOS_OPTION_NO_NDMP2
463 NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
464 struct ndmp2_auth_text *at;
466 request->auth_data.auth_type = NDMP2_AUTH_TEXT;
467 at = &request->auth_data.ndmp2_auth_data_u.auth_text;
469 at->auth_password = pw;
470 rc = NDMC_CALL(conn);
473 #endif /* !NDMOS_OPTION_NO_NDMP2 */
475 #ifndef NDMOS_OPTION_NO_NDMP3
477 NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
478 struct ndmp3_auth_text *at;
480 request->auth_data.auth_type = NDMP3_AUTH_TEXT;
481 at = &request->auth_data.ndmp3_auth_data_u.auth_text;
483 at->auth_password = pw;
484 rc = NDMC_CALL(conn);
487 #endif /* !NDMOS_OPTION_NO_NDMP3 */
489 #ifndef NDMOS_OPTION_NO_NDMP4
491 NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
492 struct ndmp4_auth_text *at;
494 request->auth_data.auth_type = NDMP4_AUTH_TEXT;
495 at = &request->auth_data.ndmp4_auth_data_u.auth_text;
497 at->auth_password = pw;
498 rc = NDMC_CALL(conn);
501 #endif /* !NDMOS_OPTION_NO_NDMP4 */
505 ndmconn_set_err_msg (conn, "connect-auth-text-failed");
513 ndmconn_auth_md5 (struct ndmconn *conn, char *id, char *pw)
516 char challenge[NDMP_MD5_CHALLENGE_LENGTH];
517 char digest[NDMP_MD5_DIGEST_LENGTH];
519 switch (conn->protocol_version) {
521 ndmconn_set_err_msg (conn, "connect-auth-md5-vers-botch");
524 #ifndef NDMOS_OPTION_NO_NDMP2
526 NDMC_WITH(ndmp2_config_get_auth_attr, NDMP2VER)
527 request->auth_type = NDMP2_AUTH_MD5;
528 rc = NDMC_CALL(conn);
530 if (reply->server_attr.auth_type != NDMP2_AUTH_MD5) {
531 ndmconn_set_err_msg (conn,
532 "connect-auth-md5-attr-type-botch");
536 reply->server_attr.ndmp2_auth_attr_u.challenge,
537 challenge, sizeof challenge);
541 #endif /* !NDMOS_OPTION_NO_NDMP2 */
543 #ifndef NDMOS_OPTION_NO_NDMP3
545 NDMC_WITH(ndmp3_config_get_auth_attr, NDMP3VER)
546 request->auth_type = NDMP3_AUTH_MD5;
547 rc = NDMC_CALL(conn);
549 if (reply->server_attr.auth_type != NDMP3_AUTH_MD5) {
550 ndmconn_set_err_msg (conn,
551 "connect-auth-md5-attr-type-botch");
555 reply->server_attr.ndmp3_auth_attr_u.challenge,
556 challenge, sizeof challenge);
560 #endif /* !NDMOS_OPTION_NO_NDMP3 */
562 #ifndef NDMOS_OPTION_NO_NDMP4
564 NDMC_WITH(ndmp4_config_get_auth_attr, NDMP4VER)
565 request->auth_type = NDMP4_AUTH_MD5;
566 rc = NDMC_CALL(conn);
568 if (reply->server_attr.auth_type != NDMP4_AUTH_MD5) {
569 ndmconn_set_err_msg (conn,
570 "connect-auth-md5-attr-type-botch");
574 reply->server_attr.ndmp4_auth_attr_u.challenge,
575 challenge, sizeof challenge);
579 #endif /* !NDMOS_OPTION_NO_NDMP4 */
583 ndmconn_set_err_msg (conn, "connect-auth-md5-attr-failed");
587 ndmmd5_digest (challenge, pw, digest);
589 switch (conn->protocol_version) {
591 ndmconn_set_err_msg (conn, "connect-auth-text-vers-botch");
594 #ifndef NDMOS_OPTION_NO_NDMP2
596 NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
597 struct ndmp2_auth_md5 *am;
599 request->auth_data.auth_type = NDMP2_AUTH_MD5;
600 am = &request->auth_data.ndmp2_auth_data_u.auth_md5;
602 NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest);
603 rc = NDMC_CALL(conn);
606 #endif /* !NDMOS_OPTION_NO_NDMP2 */
608 #ifndef NDMOS_OPTION_NO_NDMP3
610 NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
611 struct ndmp3_auth_md5 *am;
613 request->auth_data.auth_type = NDMP3_AUTH_MD5;
614 am = &request->auth_data.ndmp3_auth_data_u.auth_md5;
616 NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest);
617 rc = NDMC_CALL(conn);
620 #endif /* !NDMOS_OPTION_NO_NDMP3 */
622 #ifndef NDMOS_OPTION_NO_NDMP4
624 NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
625 struct ndmp4_auth_md5 *am;
627 request->auth_data.auth_type = NDMP4_AUTH_MD5;
628 am = &request->auth_data.ndmp4_auth_data_u.auth_md5;
630 NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest);
631 rc = NDMC_CALL(conn);
634 #endif /* !NDMOS_OPTION_NO_NDMP4 */
638 ndmconn_set_err_msg (conn, "connect-auth-md5-failed");
649 * CALL (REQUEST/REPLY), SEND, and RECEIVE
650 ****************************************************************
654 ndmconn_call (struct ndmconn *conn, struct ndmp_xa_buf *xa)
656 unsigned protocol_version = conn->protocol_version;
657 unsigned msg = xa->request.header.message;
659 struct ndmp_xdr_message_table * xmte;
661 conn->last_message = msg;
662 conn->last_call_status = NDMCONN_CALL_STATUS_BOTCH;
663 conn->last_header_error = -1; /* invalid */
664 conn->last_reply_error = -1; /* invalid */
666 if (protocol_version != xa->request.protocol_version) {
667 ndmconn_set_err_msg (conn, "protocol-version-mismatch");
668 return NDMCONN_CALL_STATUS_BOTCH;
671 xmte = ndmp_xmt_lookup (protocol_version, msg);
673 ndmconn_set_err_msg (conn, "no-xdr-found");
674 return NDMCONN_CALL_STATUS_BOTCH;
677 xa->request.header.message_type = NDMP0_MESSAGE_REQUEST;
679 if (!xmte->xdr_reply) {
680 /* no reply expected, just a send (eg NOTIFY) */
681 return ndmconn_send_nmb (conn, &xa->request);
684 rc = ndmconn_exchange_nmb (conn, &xa->request, &xa->reply);
686 ndmconn_set_err_msg (conn, "exchange-failed");
687 return NDMCONN_CALL_STATUS_BOTCH;
690 if (xa->reply.header.message != msg) {
691 ndmconn_set_err_msg (conn, "msg-mismatch");
692 return NDMCONN_CALL_STATUS_BOTCH;
695 /* TODO: this should be converted ndmp_xto9_error(....) */
696 conn->last_header_error = xa->reply.header.error;
698 if (xa->reply.header.error) {
699 conn->last_call_status = NDMCONN_CALL_STATUS_HDR_ERROR;
700 ndmconn_set_err_msg (conn, "reply-error-hdr");
701 return NDMCONN_CALL_STATUS_HDR_ERROR;
704 conn->last_reply_error = ndmnmb_get_reply_error (&xa->reply);
706 if (conn->last_reply_error != NDMP9_NO_ERR) {
707 conn->last_call_status = NDMCONN_CALL_STATUS_REPLY_ERROR;
708 ndmconn_set_err_msg (conn, "reply-error");
709 return NDMCONN_CALL_STATUS_REPLY_ERROR;
712 return NDMCONN_CALL_STATUS_OK;
716 ndmconn_exchange_nmb (struct ndmconn *conn,
717 struct ndmp_msg_buf *request_nmb,
718 struct ndmp_msg_buf *reply_nmb)
722 if ((rc = ndmconn_send_nmb (conn, request_nmb)) != 0)
724 conn->received_time = 0;
725 conn->sent_time = time(0);
728 if ((rc = ndmconn_recv_nmb (conn, reply_nmb)) != 0)
731 if (reply_nmb->header.message_type == NDMP0_MESSAGE_REPLY
732 && reply_nmb->header.reply_sequence
733 == request_nmb->header.sequence) {
734 conn->received_time = time(0);
738 (*conn->unexpected)(conn, reply_nmb);
743 ndmconn_send_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
745 return ndmconn_xdr_nmb (conn, nmb, XDR_ENCODE);
749 ndmconn_recv_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
751 NDMOS_MACRO_ZEROFILL (nmb);
752 nmb->protocol_version = conn->protocol_version;
754 return ndmconn_xdr_nmb (conn, nmb, XDR_DECODE);
758 ndmconn_free_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
765 ndmconn_xdr_nmb (struct ndmconn *conn,
766 struct ndmp_msg_buf *nmb,
769 xdrproc_t xdr_body = 0;
771 assert (conn->conn_type == NDMCONN_TYPE_REMOTE);
773 if (conn->chan.fd < 0) {
774 return ndmconn_set_err_msg (conn, "not-open");
777 conn->xdrs.x_op = x_op;
779 if (x_op == XDR_ENCODE) {
780 xdr_body = ndmnmb_find_xdrproc (nmb);
782 if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) {
783 return ndmconn_set_err_msg (conn, "unknown-body");
785 nmb->header.sequence = conn->next_sequence++;
786 nmb->header.time_stamp = time(0);
787 ndmconn_snoop_nmb (conn, nmb, "Send");
789 if (x_op == XDR_DECODE) {
790 if (!xdrrec_skiprecord (&conn->xdrs)) {
791 return ndmconn_set_err_msg (conn, "xdr-get-next");
795 if (!xdr_ndmp0_header (&conn->xdrs, &nmb->header)) {
796 ndmconn_abort (conn);
797 if (x_op == XDR_DECODE
798 && conn->chan.eof && !conn->chan.error) {
799 return ndmconn_set_err_msg (conn, "EOF");
801 return ndmconn_set_err_msg (conn, "xdr-hdr");
805 if (x_op == XDR_DECODE) {
806 xdr_body = ndmnmb_find_xdrproc (nmb);
808 if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) {
809 return ndmconn_set_err_msg (conn, "unknown-body");
812 if (nmb->header.error == NDMP0_NO_ERR) {
813 if (!(*xdr_body) (&conn->xdrs, &nmb->body)) {
814 ndmconn_abort (conn);
815 return ndmconn_set_err_msg (conn, "xdr-body");
819 if (x_op == XDR_ENCODE) {
820 if (!xdrrec_endofrecord(&conn->xdrs, 1)) {
821 ndmconn_abort (conn);
822 return ndmconn_set_err_msg (conn, "xdr-send");
825 if (x_op == XDR_DECODE) {
826 ndmconn_snoop_nmb (conn, nmb, "Recv");
836 * XDR READ/WRITE CALLBACKS
837 ****************************************************************
839 * ndmconn_readit() and ndmconn_writeit() are the XDR callbacks
840 * used by xdrrec_create(). They are fundamentally wrappers
841 * around read() and write(), and have very similar parameters.
842 * See the xdr(3) manual page (or try "man xdrrec_create").
844 * ndmconn_readit() tracks the XDR record marks, and never
845 * reads across a record boundary. This keeps select() an
846 * indicator of when there is a (single) request pending.
847 * Otherwise, we have to check buffers internal to XDR
848 * as well as the file descriptor (via select) to determine
849 * if a request is pending.
853 ndmconn_readit (void *a_conn, char *buf, int len)
855 struct ndmconn *conn = (struct ndmconn *)a_conn;
858 /* could impose timeout here */
859 if (conn->chan.fd < 0 || conn->chan.eof)
862 ndmconn_snoop (conn, 8,
863 "frag_resid=%d fhb_off=%d", conn->frag_resid, conn->fhb_off);
865 if (conn->frag_resid == 0) {
870 rc = ndmconn_sys_read (conn, (void *)(conn->frag_hdr_buf+i), c);
876 conn->frag_resid = conn->frag_hdr_buf[0] << 24;
877 conn->frag_resid |= conn->frag_hdr_buf[1] << 16;
878 conn->frag_resid |= conn->frag_hdr_buf[2] << 8;
879 conn->frag_resid |= conn->frag_hdr_buf[3];
880 conn->frag_resid &= 0xFFFFFF;
883 if (conn->fhb_off < 4) {
885 while (conn->fhb_off < 4 && len > 0) {
886 buf[i++] = conn->frag_hdr_buf[conn->fhb_off++];
892 if ((unsigned int)len > conn->frag_resid)
893 len = (unsigned int)conn->frag_resid;
895 rc = ndmconn_sys_read (conn, buf, len);
898 conn->frag_resid -= rc;
905 ndmconn_writeit (void *a_conn, char *buf, int len)
907 struct ndmconn *conn = (struct ndmconn *)a_conn;
909 /* could impose timeout here */
910 if (conn->chan.fd < 0)
913 return ndmconn_sys_write (conn, buf, len);
917 * ndmconn_sys_read() and ndmconn_sys_write() are simply
918 * wrappers around read() and write(). They implement
919 * the low-level snooping.
923 ndmconn_sys_read (struct ndmconn *conn, char *buf, unsigned len)
927 ndmconn_snoop (conn, 9, "reading %d ...", len);
929 rc = read (conn->chan.fd, buf, len);
931 ndmconn_snoop (conn, 8, "read=%d len=%d", rc, len);
932 ndmconn_hex_dump (conn, buf, rc);
937 conn->chan.error = 1;
944 ndmconn_sys_write (struct ndmconn *conn, char *buf, unsigned len)
948 ndmconn_snoop (conn, 9, "writing %d ...", len);
949 ndmconn_hex_dump (conn, buf, len);
951 rc = write (conn->chan.fd, buf, len);
953 ndmconn_snoop (conn, 8, "write=%d len=%d", rc, len);
957 conn->chan.error = 1;
968 ****************************************************************
970 * The default unexpected() handler for a connection. It is
971 * called when ndmconn_exchange_nmb() receives something
972 * other than the reply for which it is waiting.
973 * This default routine silently dumps the message.
977 ndmconn_unexpected (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
979 xdrproc_t xdr_body = ndmnmb_find_xdrproc (nmb);
982 xdr_free (xdr_body, (void*) &nmb->body);
991 ****************************************************************
993 * The ndmconn snoop stuff. The cool part. This pretty prints
994 * NDMP messages as they go flying by this end-point.
998 ndmconn_set_snoop (struct ndmconn *conn, struct ndmlog *log, int level)
1000 conn->snoop_log = log;
1001 conn->snoop_level = level;
1006 ndmconn_clear_snoop (struct ndmconn *conn)
1008 conn->snoop_log = 0;
1009 conn->snoop_level = 0;
1013 ndmconn_snoop_nmb (struct ndmconn *conn,
1014 struct ndmp_msg_buf *nmb,
1017 if (!conn->snoop_log) {
1021 ndmnmb_snoop (conn->snoop_log, conn->chan.name, conn->snoop_level,
1026 ndmconn_snoop (struct ndmconn *conn, int level, char *fmt, ...)
1030 if (conn->snoop_log && conn->snoop_level >= level) {
1032 ndmlogfv (conn->snoop_log, conn->chan.name, level, fmt, ap);
1037 /* used by ndmconn_sys_read() and ndmconn_sys_write() to show low-level */
1039 ndmconn_hex_dump (struct ndmconn *conn, char *buf, unsigned len)
1041 struct ndmlog * log = conn->snoop_log;
1042 char * tag = conn->chan.name;
1043 char linebuf[16*3+3];
1048 if (log && conn->snoop_level > 8) {
1049 for (i = 0; i < len; i++) {
1051 sprintf (p, " %02x", b);
1053 if ((i&0xF) == 0xF) {
1054 ndmlogf (log,tag,9,"%s",linebuf+1);
1059 ndmlogf (log,tag,9,"%s",linebuf+1);
1069 ****************************************************************
1071 * Possible errors for ndmconn are not enumerated.
1072 * Instead, errors are indicated by a -1 return, and
1073 * a simple string error message is available for details.
1074 * Appologies for the english-centric design, but it
1075 * is quick and easy, and better than using printf().
1079 ndmconn_set_err_msg (struct ndmconn *conn, char *err_msg)
1081 conn->last_err_msg = err_msg;
1082 ndmconn_snoop (conn, 4, "ERR=%s", err_msg);
1087 ndmconn_get_err_msg (struct ndmconn *conn)
1089 if (!conn->last_err_msg)
1090 return "-no-error-";
1092 return conn->last_err_msg;