Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndml_conn.c
1 /*
2  * Copyright (c) 1998,1999,2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
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.
15  *
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
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  */
36
37
38 #include "ndmjob.h"
39 #include "ndmlib.h"
40
41
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 */
55
56
57
58 /*
59  * INITIALIZE AND DESTRUCT
60  ****************************************************************
61  *
62  * Initialize an ndmconn. This pretty much amounts to
63  * initializing the underlying ndmchan and stuffing
64  * the function pointers.
65  */
66
67 struct ndmconn *
68 ndmconn_initialize (struct ndmconn *aconn, char *name)
69 {
70         struct ndmconn *        conn = aconn;
71
72         if (!conn) {
73                 conn = NDMOS_MACRO_NEW(struct ndmconn);
74                 if (!conn)
75                         return 0;
76         }
77
78         NDMOS_MACRO_ZEROFILL(conn);
79
80         if (!name) name = "#?";                         /* default */
81
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;
89
90         conn->call = ndmconn_call;
91
92         conn->time_limit = 0;
93
94         return conn;
95 }
96
97 /*
98  * Get rid of an ndmconn.
99  */
100 void
101 ndmconn_destruct (struct ndmconn *conn)
102 {
103         if (conn->chan.fd >= 0) {
104                 close (conn->chan.fd);
105                 conn->chan.fd = -1;
106         }
107
108         xdr_destroy (&conn->xdrs);
109
110         if (conn->was_allocated) {
111                 NDMOS_API_FREE (conn);
112                 conn = 0;
113         }
114 }
115
116 /*
117  * ESTABLISH CONNECTION
118  ****************************************************************
119  *
120  * The following four routines establish the TCP/IP connection
121  * between agents.
122  *
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
130  * ndmconn_accept()
131  *     make a connection (receive it really) from a file descriptor
132  *     already accept()ed.
133  */
134
135 int
136 ndmconn_connect_agent (struct ndmconn *conn, struct ndmagent *agent)
137 {
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;
144                 }
145                 ndmchan_start_resident (&conn->chan);
146                 return 0;
147         }
148
149         if (agent->port == 0) agent->port = NDMPPORT;
150
151         return ndmconn_connect_host_port (conn,
152                         agent->host, agent->port, agent->protocol_version);
153 }
154
155
156 int
157 ndmconn_connect_host_port (struct ndmconn *conn,
158   char * hostname, int port, unsigned want_protocol_version)
159 {
160         struct sockaddr_in      sin;
161         char *                  err = "???";
162
163         if (conn->chan.fd >= 0) {
164                 err = "already-connected";
165                 return ndmconn_set_err_msg (conn, err);
166         }
167
168         if (ndmhost_lookup (hostname, &sin) != 0) {
169                 err = "bad-host-name";
170                 return ndmconn_set_err_msg (conn, err);
171         }
172
173         if (port == 0) port = NDMPPORT;
174
175         sin.sin_port = htons(port);
176
177         return ndmconn_connect_sockaddr_in (conn, &sin, want_protocol_version);
178 }
179
180 int
181 ndmconn_connect_sockaddr_in (struct ndmconn *conn,
182   struct sockaddr_in *sin, unsigned want_protocol_version)
183 {
184         int                     fd = -1;
185         int                     rc;
186         char *                  err = "???";
187         unsigned                max_protocol_version = MAX_PROTOCOL_VERSION;
188
189         if (conn->chan.fd >= 0) {
190                 err = "already-connected";
191                 return ndmconn_set_err_msg (conn, err);
192         }
193
194         fd = socket (AF_INET, SOCK_STREAM, 0);
195         if (fd < 0) {
196                 err = malloc(1024);
197                 snprintf(err, 1023, "open a socket failed: %s", strerror(errno));
198                 goto error_out;
199         }
200
201         /* reserved port? */
202         if (connect (fd, (struct sockaddr *)sin, sizeof *sin) < 0) {
203                 err = malloc(1024);
204                 snprintf(err, 1023, "connect failed: %s", strerror(errno));
205                 goto error_out;
206         }
207
208         ndmchan_start_readchk (&conn->chan, fd);
209         conn->conn_type = NDMCONN_TYPE_REMOTE;
210
211         /*
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.
215          */
216         NDMC_WITH_NO_REPLY(ndmp0_notify_connected,0)
217                 rc = ndmconn_recv_nmb(conn, &xa->request);
218                 if (rc != 0) {
219                         err = "recv-notify-connected";
220                         goto error_out;
221                 }
222                 if (xa->request.header.message_type != NDMP0_MESSAGE_REQUEST
223                  || xa->request.header.message != NDMP0_NOTIFY_CONNECTED) {
224                         err = "msg-not-notify-connected";
225                         goto error_out;
226                 }
227
228                 if (request->reason != NDMP0_CONNECTED) {
229                         err = "notify-connected-not-connected";
230                         goto error_out;
231                 }
232
233                 if (max_protocol_version > request->protocol_version) {
234                         max_protocol_version = request->protocol_version;
235                 }
236         NDMC_ENDWITH
237
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";
242                 goto error_out;
243         }
244
245         /*
246          * Send the OPEN request
247          */
248         NDMC_WITH(ndmp0_connect_open,0)
249                 request->protocol_version = want_protocol_version;
250                 rc = NDMC_CALL(conn);
251                 if (rc) {
252                         err = "connect-open-failed";
253                         goto error_out;
254                 }
255         NDMC_ENDWITH
256
257         /* GOOD! */
258
259         conn->protocol_version = want_protocol_version;
260
261         return 0;
262
263   error_out:
264         if (fd >= 0) {
265                 close (fd);
266                 fd = -1;
267         }
268         conn->chan.fd = -1;
269         conn->chan.mode = NDMCHAN_MODE_IDLE;
270         conn->conn_type = NDMCONN_TYPE_NONE;
271
272         return ndmconn_set_err_msg (conn, err);
273 }
274
275 int
276 ndmconn_try_open (struct ndmconn *conn, unsigned protocol_version)
277 {
278         int             rc;
279
280         /*
281          * Send the OPEN request
282          */
283         NDMC_WITH(ndmp0_connect_open,0)
284                 request->protocol_version = protocol_version;
285                 rc = NDMC_CALL(conn);
286                 if (rc) {
287                         ndmconn_set_err_msg (conn, "connect-open-failed");
288                 }
289         NDMC_ENDWITH
290
291         return rc;
292 }
293
294 int
295 ndmconn_accept (struct ndmconn *conn, int sock)
296 {
297         char *                  err = "???";
298
299         if (conn->chan.fd >= 0) {
300                 err = "already-connected";
301                 return ndmconn_set_err_msg (conn, err);
302         }
303
304         ndmchan_start_readchk (&conn->chan, sock);
305         conn->conn_type = NDMCONN_TYPE_REMOTE;
306
307         /*
308          * Send the NDMP_NOTIFY_CONNECTED message, no reply
309          * The connect()er is waiting for it.
310          */
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";
315                 NDMC_SEND(conn);
316         NDMC_ENDWITH
317
318         /* assume connection is running in offered protocol_version */
319         conn->protocol_version = MAX_PROTOCOL_VERSION;
320
321         return 0;
322 }
323
324
325
326
327 /*
328  * TERMINATE CONNECTION
329  ****************************************************************
330  *
331  * These two routines are about terminating a connection.
332  * They are incomplete.
333  */
334
335 /* hangup */
336 int
337 ndmconn_abort (struct ndmconn *conn)
338 {
339         return 0;
340 }
341
342 /* orderly close */
343 int
344 ndmconn_close (struct ndmconn *conn)
345 {
346         return 0;
347 }
348
349
350 /*
351  * Return the underlying fd of the ndmconn.
352  * This is no longer used since the ndmchan stuff was done.
353  */
354
355 int
356 ndmconn_fileno (struct ndmconn *conn)
357 {
358         return conn->chan.fd;
359 }
360
361
362
363
364
365 /*
366  * AUTHENTICATION
367  *
368  * The following three routines do the NDMP_CONNECT_AUTH sequences.
369  */
370
371 int
372 ndmconn_auth_agent (struct ndmconn *conn, struct ndmagent *agent)
373 {
374         int             rc;
375
376         if (conn->conn_type == NDMCONN_TYPE_RESIDENT)
377                 return 0;
378
379         switch (agent->auth_type) {
380         case 'n':               /* NDMP_AUTH_NONE */
381                 rc = ndmconn_auth_none (conn);
382                 break;
383
384         case 't':               /* NDMP_AUTH_TEXT */
385                 rc = ndmconn_auth_text (conn, agent->account, agent->password);
386                 break;
387
388         case 'm':               /* NDMP_AUTH_MD5 */
389                 rc = ndmconn_auth_md5 (conn, agent->account, agent->password);
390                 break;
391
392         case 'v':               /* void (don't auth) */
393                 rc = 0;
394                 break;
395
396         default:
397                 ndmconn_set_err_msg (conn, "connect-auth-unknown");
398                 rc = -1;
399                 break;
400         }
401
402         return rc;
403 }
404
405 int
406 ndmconn_auth_none (struct ndmconn *conn)
407 {
408         int             rc;
409
410         switch (conn->protocol_version) {
411         default:
412                 ndmconn_set_err_msg (conn, "connect-auth-none-vers-botch");
413                 return -1;
414
415 #ifndef NDMOS_OPTION_NO_NDMP2
416         case NDMP2VER:
417             NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
418                 request->auth_data.auth_type = NDMP2_AUTH_NONE;
419                 rc = NDMC_CALL(conn);
420             NDMC_ENDWITH
421             break;
422 #endif /* !NDMOS_OPTION_NO_NDMP2 */
423
424 #ifndef NDMOS_OPTION_NO_NDMP3
425         case NDMP3VER:
426             NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
427                 request->auth_data.auth_type = NDMP3_AUTH_NONE;
428                 rc = NDMC_CALL(conn);
429             NDMC_ENDWITH
430             break;
431 #endif /* !NDMOS_OPTION_NO_NDMP3 */
432
433 #ifndef NDMOS_OPTION_NO_NDMP4
434         case NDMP4VER:
435             NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
436                 request->auth_data.auth_type = NDMP4_AUTH_NONE;
437                 rc = NDMC_CALL(conn);
438             NDMC_ENDWITH
439             break;
440 #endif /* !NDMOS_OPTION_NO_NDMP4 */
441         }
442
443         if (rc) {
444                 ndmconn_set_err_msg (conn, "connect-auth-none-failed");
445                 return -1;
446         }
447
448         return 0;
449 }
450
451 int
452 ndmconn_auth_text (struct ndmconn *conn, char *id, char *pw)
453 {
454         int             rc;
455
456         switch (conn->protocol_version) {
457         default:
458                 ndmconn_set_err_msg (conn, "connect-auth-text-vers-botch");
459                 return -1;
460
461 #ifndef NDMOS_OPTION_NO_NDMP2
462         case NDMP2VER:
463             NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
464                 struct ndmp2_auth_text *at;
465
466                 request->auth_data.auth_type = NDMP2_AUTH_TEXT;
467                 at = &request->auth_data.ndmp2_auth_data_u.auth_text;
468                 at->auth_id = id;
469                 at->auth_password = pw;
470                 rc = NDMC_CALL(conn);
471             NDMC_ENDWITH
472             break;
473 #endif /* !NDMOS_OPTION_NO_NDMP2 */
474
475 #ifndef NDMOS_OPTION_NO_NDMP3
476         case NDMP3VER:
477             NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
478                 struct ndmp3_auth_text *at;
479
480                 request->auth_data.auth_type = NDMP3_AUTH_TEXT;
481                 at = &request->auth_data.ndmp3_auth_data_u.auth_text;
482                 at->auth_id = id;
483                 at->auth_password = pw;
484                 rc = NDMC_CALL(conn);
485             NDMC_ENDWITH
486             break;
487 #endif /* !NDMOS_OPTION_NO_NDMP3 */
488
489 #ifndef NDMOS_OPTION_NO_NDMP4
490         case NDMP4VER:
491             NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
492                 struct ndmp4_auth_text *at;
493
494                 request->auth_data.auth_type = NDMP4_AUTH_TEXT;
495                 at = &request->auth_data.ndmp4_auth_data_u.auth_text;
496                 at->auth_id = id;
497                 at->auth_password = pw;
498                 rc = NDMC_CALL(conn);
499             NDMC_ENDWITH
500             break;
501 #endif /* !NDMOS_OPTION_NO_NDMP4 */
502         }
503
504         if (rc) {
505                 ndmconn_set_err_msg (conn, "connect-auth-text-failed");
506                 return -1;
507         }
508
509         return 0;
510 }
511
512 int
513 ndmconn_auth_md5 (struct ndmconn *conn, char *id, char *pw)
514 {
515         int             rc;
516         char            challenge[NDMP_MD5_CHALLENGE_LENGTH];
517         char            digest[NDMP_MD5_DIGEST_LENGTH];
518
519         switch (conn->protocol_version) {
520         default:
521                 ndmconn_set_err_msg (conn, "connect-auth-md5-vers-botch");
522                 return -1;
523
524 #ifndef NDMOS_OPTION_NO_NDMP2
525         case NDMP2VER:
526             NDMC_WITH(ndmp2_config_get_auth_attr, NDMP2VER)
527                 request->auth_type = NDMP2_AUTH_MD5;
528                 rc = NDMC_CALL(conn);
529                 if (rc == 0) {
530                         if (reply->server_attr.auth_type != NDMP2_AUTH_MD5) {
531                                 ndmconn_set_err_msg (conn,
532                                         "connect-auth-md5-attr-type-botch");
533                                 return -1;
534                         }
535                         NDMOS_API_BCOPY (
536                                 reply->server_attr.ndmp2_auth_attr_u.challenge,
537                                 challenge, sizeof challenge);
538                 }
539             NDMC_ENDWITH
540             break;
541 #endif /* !NDMOS_OPTION_NO_NDMP2 */
542
543 #ifndef NDMOS_OPTION_NO_NDMP3
544         case NDMP3VER:
545             NDMC_WITH(ndmp3_config_get_auth_attr, NDMP3VER)
546                 request->auth_type = NDMP3_AUTH_MD5;
547                 rc = NDMC_CALL(conn);
548                 if (rc == 0) {
549                         if (reply->server_attr.auth_type != NDMP3_AUTH_MD5) {
550                                 ndmconn_set_err_msg (conn,
551                                         "connect-auth-md5-attr-type-botch");
552                                 return -1;
553                         }
554                         NDMOS_API_BCOPY (
555                                 reply->server_attr.ndmp3_auth_attr_u.challenge,
556                                 challenge, sizeof challenge);
557                 }
558             NDMC_ENDWITH
559             break;
560 #endif /* !NDMOS_OPTION_NO_NDMP3 */
561
562 #ifndef NDMOS_OPTION_NO_NDMP4
563         case NDMP4VER:
564             NDMC_WITH(ndmp4_config_get_auth_attr, NDMP4VER)
565                 request->auth_type = NDMP4_AUTH_MD5;
566                 rc = NDMC_CALL(conn);
567                 if (rc == 0) {
568                         if (reply->server_attr.auth_type != NDMP4_AUTH_MD5) {
569                                 ndmconn_set_err_msg (conn,
570                                         "connect-auth-md5-attr-type-botch");
571                                 return -1;
572                         }
573                         NDMOS_API_BCOPY (
574                                 reply->server_attr.ndmp4_auth_attr_u.challenge,
575                                 challenge, sizeof challenge);
576                 }
577             NDMC_ENDWITH
578             break;
579 #endif /* !NDMOS_OPTION_NO_NDMP4 */
580         }
581
582         if (rc) {
583                 ndmconn_set_err_msg (conn, "connect-auth-md5-attr-failed");
584                 return -1;
585         }
586
587         ndmmd5_digest (challenge, pw, digest);
588
589         switch (conn->protocol_version) {
590         default:
591                 ndmconn_set_err_msg (conn, "connect-auth-text-vers-botch");
592                 return -1;
593
594 #ifndef NDMOS_OPTION_NO_NDMP2
595         case NDMP2VER:
596             NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
597                 struct ndmp2_auth_md5 *am;
598
599                 request->auth_data.auth_type = NDMP2_AUTH_MD5;
600                 am = &request->auth_data.ndmp2_auth_data_u.auth_md5;
601                 am->auth_id = id;
602                 NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest);
603                 rc = NDMC_CALL(conn);
604             NDMC_ENDWITH
605             break;
606 #endif /* !NDMOS_OPTION_NO_NDMP2 */
607
608 #ifndef NDMOS_OPTION_NO_NDMP3
609         case NDMP3VER:
610             NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
611                 struct ndmp3_auth_md5 *am;
612
613                 request->auth_data.auth_type = NDMP3_AUTH_MD5;
614                 am = &request->auth_data.ndmp3_auth_data_u.auth_md5;
615                 am->auth_id = id;
616                 NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest);
617                 rc = NDMC_CALL(conn);
618             NDMC_ENDWITH
619             break;
620 #endif /* !NDMOS_OPTION_NO_NDMP3 */
621
622 #ifndef NDMOS_OPTION_NO_NDMP4
623         case NDMP4VER:
624             NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
625                 struct ndmp4_auth_md5 *am;
626
627                 request->auth_data.auth_type = NDMP4_AUTH_MD5;
628                 am = &request->auth_data.ndmp4_auth_data_u.auth_md5;
629                 am->auth_id = id;
630                 NDMOS_API_BCOPY (digest, am->auth_digest, sizeof digest);
631                 rc = NDMC_CALL(conn);
632             NDMC_ENDWITH
633             break;
634 #endif /* !NDMOS_OPTION_NO_NDMP4 */
635         }
636
637         if (rc) {
638                 ndmconn_set_err_msg (conn, "connect-auth-md5-failed");
639                 return -1;
640         }
641
642         return 0;
643 }
644
645
646
647
648 /*
649  * CALL (REQUEST/REPLY), SEND, and RECEIVE
650  ****************************************************************
651  */
652
653 int
654 ndmconn_call (struct ndmconn *conn, struct ndmp_xa_buf *xa)
655 {
656         unsigned                protocol_version = conn->protocol_version;
657         unsigned                msg = xa->request.header.message;
658         int                     rc;
659         struct ndmp_xdr_message_table * xmte;
660
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 */
665
666         if (protocol_version != xa->request.protocol_version) {
667                 ndmconn_set_err_msg (conn, "protocol-version-mismatch");
668                 return NDMCONN_CALL_STATUS_BOTCH;
669         }
670
671         xmte = ndmp_xmt_lookup (protocol_version, msg);
672         if (!xmte) {
673                 ndmconn_set_err_msg (conn, "no-xdr-found");
674                 return NDMCONN_CALL_STATUS_BOTCH;
675         }
676
677         xa->request.header.message_type = NDMP0_MESSAGE_REQUEST;
678
679         if (!xmte->xdr_reply) {
680                 /* no reply expected, just a send (eg NOTIFY) */
681                 return ndmconn_send_nmb (conn, &xa->request);
682         }
683
684         rc = ndmconn_exchange_nmb (conn, &xa->request, &xa->reply);
685         if (rc) {
686                 ndmconn_set_err_msg (conn, "exchange-failed");
687                 return NDMCONN_CALL_STATUS_BOTCH;
688         }
689
690         if (xa->reply.header.message != msg) {
691                 ndmconn_set_err_msg (conn, "msg-mismatch");
692                 return NDMCONN_CALL_STATUS_BOTCH;
693         }
694
695         /* TODO: this should be converted ndmp_xto9_error(....) */
696         conn->last_header_error = xa->reply.header.error;
697
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;
702         }
703
704         conn->last_reply_error = ndmnmb_get_reply_error (&xa->reply);
705
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;
710         }
711
712         return NDMCONN_CALL_STATUS_OK;
713 }
714
715 int
716 ndmconn_exchange_nmb (struct ndmconn *conn,
717   struct ndmp_msg_buf *request_nmb,
718   struct ndmp_msg_buf *reply_nmb)
719 {
720         int                     rc;
721
722         if ((rc = ndmconn_send_nmb (conn, request_nmb)) != 0)
723                 return rc;
724         conn->received_time = 0;
725         conn->sent_time = time(0);
726
727         for (;;) {
728                 if ((rc = ndmconn_recv_nmb (conn, reply_nmb)) != 0)
729                         return rc;
730
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);
735                         return 0;
736                 }
737
738                 (*conn->unexpected)(conn, reply_nmb);
739         }
740 }
741
742 int
743 ndmconn_send_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
744 {
745         return ndmconn_xdr_nmb (conn, nmb, XDR_ENCODE);
746 }
747
748 int
749 ndmconn_recv_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
750 {
751         NDMOS_MACRO_ZEROFILL (nmb);
752         nmb->protocol_version = conn->protocol_version;
753
754         return ndmconn_xdr_nmb (conn, nmb, XDR_DECODE);
755 }
756
757 void
758 ndmconn_free_nmb (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
759 {
760         ndmnmb_free (nmb);
761 }
762
763
764 int
765 ndmconn_xdr_nmb (struct ndmconn *conn,
766   struct ndmp_msg_buf *nmb,
767   enum xdr_op x_op)
768 {
769         xdrproc_t xdr_body = 0;
770
771         assert (conn->conn_type == NDMCONN_TYPE_REMOTE);
772
773         if (conn->chan.fd < 0) {
774                 return ndmconn_set_err_msg (conn, "not-open");
775         }
776
777         conn->xdrs.x_op = x_op;
778
779         if (x_op == XDR_ENCODE) {
780                 xdr_body = ndmnmb_find_xdrproc (nmb);
781
782                 if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) {
783                         return ndmconn_set_err_msg (conn, "unknown-body");
784                 }
785                 nmb->header.sequence = conn->next_sequence++;
786                 nmb->header.time_stamp = time(0);
787                 ndmconn_snoop_nmb (conn, nmb, "Send");
788         }
789         if (x_op == XDR_DECODE) {
790                 if (!xdrrec_skiprecord (&conn->xdrs)) {
791                         return ndmconn_set_err_msg (conn, "xdr-get-next");
792                 }
793         }
794
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");
800                 } else {
801                         return ndmconn_set_err_msg (conn, "xdr-hdr");
802                 }
803         }
804
805         if (x_op == XDR_DECODE) {
806                 xdr_body = ndmnmb_find_xdrproc (nmb);
807
808                 if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) {
809                         return ndmconn_set_err_msg (conn, "unknown-body");
810                 }
811         }
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");
816                 }
817         }
818
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");
823                 }
824         }
825         if (x_op == XDR_DECODE) {
826                 ndmconn_snoop_nmb (conn, nmb, "Recv");
827         }
828
829         return 0;
830 }
831
832
833
834
835 /*
836  * XDR READ/WRITE CALLBACKS
837  ****************************************************************
838  *
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").
843  *
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.
850  */
851
852 int
853 ndmconn_readit (void *a_conn, char *buf, int len)
854 {
855         struct ndmconn *conn = (struct ndmconn *)a_conn;
856         int             rc, i, c;
857
858         /* could impose timeout here */
859         if (conn->chan.fd < 0 || conn->chan.eof)
860                 return -1;
861
862         ndmconn_snoop (conn, 8,
863                 "frag_resid=%d fhb_off=%d", conn->frag_resid, conn->fhb_off);
864
865         if (conn->frag_resid == 0) {
866                 i = 0;
867                 while (i < 4) {
868                         c = 4 - i;
869
870                         rc = ndmconn_sys_read (conn, (void *)(conn->frag_hdr_buf+i), c);
871                         if (rc <= 0) {
872                                 return rc;
873                         }
874                         i += rc;
875                 }
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;
881                 conn->fhb_off = 0;
882         }
883         if (conn->fhb_off < 4) {
884                 i = 0;
885                 while (conn->fhb_off < 4 && len > 0) {
886                         buf[i++] = conn->frag_hdr_buf[conn->fhb_off++];
887                         len--;
888                 }
889                 return i;
890         }
891
892         if ((unsigned int)len > conn->frag_resid)
893                 len = (unsigned int)conn->frag_resid;
894
895         rc = ndmconn_sys_read (conn, buf, len);
896
897         if (rc > 0) {
898                 conn->frag_resid -= rc;
899         }
900
901         return rc;
902 }
903
904 int
905 ndmconn_writeit (void *a_conn, char *buf, int len)
906 {
907         struct ndmconn *conn = (struct ndmconn *)a_conn;
908
909         /* could impose timeout here */
910         if (conn->chan.fd < 0)
911                 return -1;
912
913         return ndmconn_sys_write (conn, buf, len);
914 }
915
916 /*
917  * ndmconn_sys_read() and ndmconn_sys_write() are simply
918  * wrappers around read() and write(). They implement
919  * the low-level snooping.
920  */
921
922 int
923 ndmconn_sys_read (struct ndmconn *conn, char *buf, unsigned len)
924 {
925         int             rc;
926
927         ndmconn_snoop (conn, 9, "reading %d ...", len);
928
929         rc = read (conn->chan.fd, buf, len);
930
931         ndmconn_snoop (conn, 8, "read=%d len=%d", rc, len);
932         ndmconn_hex_dump (conn, buf, rc);
933
934         if (rc <= 0) {
935                 conn->chan.eof = 1;
936                 if (rc < 0)
937                         conn->chan.error = 1;
938         }
939
940         return rc;
941 }
942
943 int
944 ndmconn_sys_write (struct ndmconn *conn, char *buf, unsigned len)
945 {
946         int             rc;
947
948         ndmconn_snoop (conn, 9, "writing %d ...", len);
949         ndmconn_hex_dump (conn, buf, len);
950
951         rc = write (conn->chan.fd, buf, len);
952
953         ndmconn_snoop (conn, 8, "write=%d len=%d", rc, len);
954
955         if (rc !=(int)len) {
956                 conn->chan.eof = 1;
957                 conn->chan.error = 1;
958         }
959
960         return rc;
961 }
962
963
964
965
966 /*
967  * UNEXPECTED
968  ****************************************************************
969  *
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.
974  */
975
976 void
977 ndmconn_unexpected (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
978 {
979         xdrproc_t xdr_body = ndmnmb_find_xdrproc (nmb);
980
981         if (xdr_body) {
982                 xdr_free (xdr_body, (void*) &nmb->body);
983         }
984 }
985
986
987
988
989 /*
990  * SNOOP
991  ****************************************************************
992  *
993  * The ndmconn snoop stuff. The cool part. This pretty prints
994  * NDMP messages as they go flying by this end-point.
995  */
996
997 int
998 ndmconn_set_snoop (struct ndmconn *conn, struct ndmlog *log, int level)
999 {
1000         conn->snoop_log = log;
1001         conn->snoop_level = level;
1002         return 0;
1003 }
1004
1005 void
1006 ndmconn_clear_snoop (struct ndmconn *conn)
1007 {
1008         conn->snoop_log = 0;
1009         conn->snoop_level = 0;
1010 }
1011
1012 void
1013 ndmconn_snoop_nmb (struct ndmconn *conn,
1014   struct ndmp_msg_buf *nmb,
1015   char *whence)
1016 {
1017         if (!conn->snoop_log) {
1018                 return;
1019         }
1020
1021         ndmnmb_snoop (conn->snoop_log, conn->chan.name, conn->snoop_level,
1022                                 nmb, whence);
1023 }
1024
1025 void
1026 ndmconn_snoop (struct ndmconn *conn, int level, char *fmt, ...)
1027 {
1028         va_list         ap;
1029
1030         if (conn->snoop_log && conn->snoop_level >= level) {
1031                 va_start (ap, fmt);
1032                 ndmlogfv (conn->snoop_log, conn->chan.name, level, fmt, ap);
1033                 va_end (ap);
1034         }
1035 }
1036
1037 /* used by ndmconn_sys_read() and ndmconn_sys_write() to show low-level */
1038 void
1039 ndmconn_hex_dump (struct ndmconn *conn, char *buf, unsigned len)
1040 {
1041         struct ndmlog * log = conn->snoop_log;
1042         char *          tag = conn->chan.name;
1043         char            linebuf[16*3+3];
1044         char *          p = linebuf;
1045         int             b;
1046         unsigned        i;
1047
1048         if (log && conn->snoop_level > 8) {
1049                 for (i = 0; i < len; i++) {
1050                         b = buf[i] & 0xFF;
1051                         sprintf (p, " %02x", b);
1052                         while (*p) p++;
1053                         if ((i&0xF) == 0xF) {
1054                                 ndmlogf (log,tag,9,"%s",linebuf+1);
1055                                 p = linebuf;
1056                         }
1057                 }
1058                 if (p > linebuf) {
1059                         ndmlogf (log,tag,9,"%s",linebuf+1);
1060                 }
1061         }
1062 }
1063
1064
1065
1066
1067 /*
1068  * ERRORS
1069  ****************************************************************
1070  *
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().
1076  */
1077
1078 int
1079 ndmconn_set_err_msg (struct ndmconn *conn, char *err_msg)
1080 {
1081         conn->last_err_msg = err_msg;
1082         ndmconn_snoop (conn, 4, "ERR=%s", err_msg);
1083         return -1;
1084 }
1085
1086 char *
1087 ndmconn_get_err_msg (struct ndmconn *conn)
1088 {
1089         if (!conn->last_err_msg)
1090                 return "-no-error-";
1091         else
1092                 return conn->last_err_msg;
1093 }