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 "ndmlib.h"
39 
40 
41 #ifndef NDMOS_OPTION_NO_NDMP4
42 #define MAX_PROTOCOL_VERSION NDMP4VER
43 #else /* !NDMOS_OPTION_NO_NDMP4 */
44 #ifndef NDMOS_OPTION_NO_NDMP3
45 #define MAX_PROTOCOL_VERSION NDMP3VER
46 #else /* !NDMOS_OPTION_NO_NDMP3 */
47 #ifndef NDMOS_OPTION_NO_NDMP2
48 #define MAX_PROTOCOL_VERSION NDMP2VER
49 #else /* !NDMOS_OPTION_NO_NDMP2 */
50 #define MAX_PROTOCOL_VERSION 0
51 #endif /* !NDMOS_OPTION_NO_NDM2 */
52 #endif /* !NDMOS_OPTION_NO_NDMP3 */
53 #endif /* !NDMOS_OPTION_NO_NDMP4 */
54 
55 
56 /*
57  * INITIALIZE AND DESTRUCT
58  ****************************************************************
59  *
60  * Initialize an ndmconn. This pretty much amounts to
61  * initializing the underlying ndmchan and stuffing
62  * the function pointers.
63  */
64 
ndmconn_initialize(struct ndmconn * aconn,char * name)65 struct ndmconn* ndmconn_initialize(struct ndmconn* aconn, char* name)
66 {
67   struct ndmconn* conn = aconn;
68 
69   if (!conn) {
70     conn = NDMOS_MACRO_NEW(struct ndmconn);
71     if (!conn) return 0;
72   }
73 
74   NDMOS_MACRO_ZEROFILL(conn);
75 
76   if (!name) name = "#?"; /* default */
77 
78   ndmchan_initialize(&conn->chan, name);
79   conn->was_allocated = aconn == 0;
80   conn->next_sequence = 1;
81   xdrrec_create(&conn->xdrs, 0, 0, (void*)conn, (void*)ndmconn_readit,
82                 (void*)ndmconn_writeit);
83   conn->unexpected = ndmconn_unexpected;
84 
85   conn->call = ndmconn_call;
86 
87   conn->time_limit = 0;
88 
89   return conn;
90 }
91 
92 /*
93  * Get rid of an ndmconn.
94  */
ndmconn_destruct(struct ndmconn * conn)95 void ndmconn_destruct(struct ndmconn* conn)
96 {
97   if (conn->chan.fd >= 0) {
98     close(conn->chan.fd);
99     conn->chan.fd = -1;
100   }
101 
102   if (conn->xdrs.x_ops) {
103     xdr_destroy(&conn->xdrs);
104     conn->xdrs.x_ops = NULL;
105   }
106 
107   if (conn->was_allocated) {
108     conn->was_allocated = 0;
109     NDMOS_API_FREE(conn);
110     conn = 0;
111   }
112 }
113 
114 /*
115  * ESTABLISH CONNECTION
116  ****************************************************************
117  *
118  * The following four routines establish the TCP/IP connection
119  * between agents.
120  *
121  * ndmconn_connect_agent()
122  *     make a connection per an ndmagent, uses ..._host_port()
123  * ndmconn_connect_host_port ()
124  *     make a connection per a hostname and port, uses ..._sockaddr_in()
125  * ndmconn_connect_sockaddr_in()
126  *     make a connection per sockaddr_in, performs NDMP_CONNECT_
127  *     sequences, but no authentication
128  * ndmconn_accept()
129  *     make a connection (receive it really) from a file descriptor
130  *     already accept()ed.
131  */
132 
ndmconn_connect_agent(struct ndmconn * conn,struct ndmagent * agent)133 int ndmconn_connect_agent(struct ndmconn* conn, struct ndmagent* agent)
134 {
135   if (agent->conn_type == NDMCONN_TYPE_RESIDENT) {
136     conn->conn_type = NDMCONN_TYPE_RESIDENT;
137     conn->protocol_version = agent->protocol_version;
138     if (conn->protocol_version == 0) {
139       /* Let's negotiate......MAX */
140       conn->protocol_version = MAX_PROTOCOL_VERSION;
141     }
142     ndmchan_start_resident(&conn->chan);
143     return 0;
144   }
145 
146   if (agent->port == 0) agent->port = NDMPPORT;
147 
148   return ndmconn_connect_host_port(conn, agent->host, agent->port,
149                                    agent->protocol_version);
150 }
151 
152 
ndmconn_connect_host_port(struct ndmconn * conn,char * hostname,int port,unsigned want_protocol_version)153 int ndmconn_connect_host_port(struct ndmconn* conn,
154                               char* hostname,
155                               int port,
156                               unsigned want_protocol_version)
157 {
158   struct sockaddr_in sin;
159   char* err = "???";
160 
161   if (conn->chan.fd >= 0) {
162     err = "already-connected";
163     return ndmconn_set_err_msg(conn, err);
164   }
165 
166   if (ndmhost_lookup(hostname, &sin) != 0) {
167     err = "bad-host-name";
168     return ndmconn_set_err_msg(conn, err);
169   }
170 
171   if (port == 0) port = NDMPPORT;
172 
173   sin.sin_port = htons(port);
174 
175   return ndmconn_connect_sockaddr_in(conn, &sin, want_protocol_version);
176 }
177 
ndmconn_connect_sockaddr_in(struct ndmconn * conn,struct sockaddr_in * sin,unsigned want_protocol_version)178 int ndmconn_connect_sockaddr_in(struct ndmconn* conn,
179                                 struct sockaddr_in* sin,
180                                 unsigned want_protocol_version)
181 {
182   int fd = -1;
183   int rc;
184   char* err = "???";
185   unsigned max_protocol_version = MAX_PROTOCOL_VERSION;
186 
187   if (conn->chan.fd >= 0) {
188     err = "already-connected";
189     return ndmconn_set_err_msg(conn, err);
190   }
191 
192   fd = socket(AF_INET, SOCK_STREAM, 0);
193   if (fd < 0) {
194     err = NDMOS_API_MALLOC(1024);
195     if (err) snprintf(err, 1023, "open a socket failed: %s", strerror(errno));
196     goto error_out;
197   }
198 
199   /* reserved port? */
200   if (connect(fd, (struct sockaddr*)sin, sizeof *sin) < 0) {
201     err = NDMOS_API_MALLOC(1024);
202     if (err) snprintf(err, 1023, "connect failed: %s", strerror(errno));
203     goto error_out;
204   }
205 
206   ndmchan_start_readchk(&conn->chan, fd);
207   conn->conn_type = NDMCONN_TYPE_REMOTE;
208 
209   /*
210    * Await the NDMP_NOTIFY_CONNECTED request (no reply)
211    * Don't get confused that this client-side is awaiting
212    * a "request" from the server.
213    */
214   NDMC_WITH_NO_REPLY(ndmp0_notify_connected, 0)
215   rc = ndmconn_recv_nmb(conn, &xa->request);
216   if (rc != 0) {
217     err = "recv-notify-connected";
218     goto error_out;
219   }
220   if (xa->request.header.message_type != NDMP0_MESSAGE_REQUEST ||
221       xa->request.header.message != NDMP0_NOTIFY_CONNECTED) {
222     err = "msg-not-notify-connected";
223     goto error_out;
224   }
225 
226   if (request->reason != NDMP0_CONNECTED) {
227     err = "notify-connected-not-connected";
228     goto error_out;
229   }
230 
231   if (max_protocol_version > request->protocol_version) {
232     max_protocol_version = request->protocol_version;
233   }
234   NDMC_ENDWITH
235 
236   if (want_protocol_version == 0) {
237     want_protocol_version = max_protocol_version;
238   } else if (want_protocol_version > max_protocol_version) {
239     err = "connect-want/max-version-mismatch";
240     goto error_out;
241   }
242 
243   /*
244    * Send the OPEN request
245    */
246   NDMC_WITH(ndmp0_connect_open, 0)
247   request->protocol_version = want_protocol_version;
248   rc = NDMC_CALL(conn);
249   if (rc) {
250     err = "connect-open-failed";
251     goto error_out;
252   }
253   NDMC_ENDWITH
254 
255   /* GOOD! */
256 
257   conn->protocol_version = want_protocol_version;
258 
259   return 0;
260 
261 error_out:
262   if (fd >= 0) {
263     close(fd);
264     fd = -1;
265   }
266   conn->chan.fd = -1;
267   conn->chan.mode = NDMCHAN_MODE_IDLE;
268   conn->conn_type = NDMCONN_TYPE_NONE;
269 
270   return ndmconn_set_err_msg(conn, err);
271 }
272 
ndmconn_try_open(struct ndmconn * conn,unsigned protocol_version)273 int ndmconn_try_open(struct ndmconn* conn, unsigned protocol_version)
274 {
275   int rc;
276 
277   /*
278    * Send the OPEN request
279    */
280   NDMC_WITH(ndmp0_connect_open, 0)
281   request->protocol_version = protocol_version;
282   rc = NDMC_CALL(conn);
283   if (rc) { ndmconn_set_err_msg(conn, "connect-open-failed"); }
284   NDMC_ENDWITH
285 
286   return rc;
287 }
288 
ndmconn_accept(struct ndmconn * conn,int sock)289 int ndmconn_accept(struct ndmconn* conn, int sock)
290 {
291   char* err = "???";
292 
293   if (conn->chan.fd >= 0) {
294     err = "already-connected";
295     return ndmconn_set_err_msg(conn, err);
296   }
297 
298   ndmchan_start_readchk(&conn->chan, sock);
299   conn->conn_type = NDMCONN_TYPE_REMOTE;
300 
301   /*
302    * Send the NDMP_NOTIFY_CONNECTED message, no reply
303    * The connect()er is waiting for it.
304    */
305   NDMC_WITH_NO_REPLY(ndmp0_notify_connected, 0)
306   request->reason = NDMP0_CONNECTED;
307   request->protocol_version = MAX_PROTOCOL_VERSION;
308   request->text_reason = "Hello";
309   NDMC_SEND(conn);
310   NDMC_ENDWITH
311 
312   /* assume connection is running in offered protocol_version */
313   conn->protocol_version = MAX_PROTOCOL_VERSION;
314 
315   return 0;
316 }
317 
318 
319 /*
320  * TERMINATE CONNECTION
321  ****************************************************************
322  *
323  * These two routines are about terminating a connection.
324  * They are incomplete.
325  */
326 
327 /* hangup */
ndmconn_abort(struct ndmconn * conn)328 int ndmconn_abort(struct ndmconn* conn) { return 0; }
329 
330 /* orderly close */
ndmconn_close(struct ndmconn * conn)331 int ndmconn_close(struct ndmconn* conn) { return 0; }
332 
333 
334 /*
335  * Return the underlying fd of the ndmconn.
336  * This is no longer used since the ndmchan stuff was done.
337  */
338 
ndmconn_fileno(struct ndmconn * conn)339 int ndmconn_fileno(struct ndmconn* conn) { return conn->chan.fd; }
340 
341 
342 /*
343  * AUTHENTICATION
344  *
345  * The following three routines do the NDMP_CONNECT_AUTH sequences.
346  */
347 
ndmconn_auth_agent(struct ndmconn * conn,struct ndmagent * agent)348 int ndmconn_auth_agent(struct ndmconn* conn, struct ndmagent* agent)
349 {
350   int rc;
351 
352   if (conn->conn_type == NDMCONN_TYPE_RESIDENT) return 0;
353 
354   switch (agent->auth_type) {
355     case 'n': /* NDMP_AUTH_NONE */
356       rc = ndmconn_auth_none(conn);
357       break;
358 
359     case 't': /* NDMP_AUTH_TEXT */
360       rc = ndmconn_auth_text(conn, agent->account, agent->password);
361       break;
362 
363     case 'm': /* NDMP_AUTH_MD5 */
364       rc = ndmconn_auth_md5(conn, agent->account, agent->password);
365       break;
366 
367     case 'v': /* void (don't auth) */
368       rc = 0;
369       break;
370 
371     default:
372       ndmconn_set_err_msg(conn, "connect-auth-unknown");
373       rc = -1;
374       break;
375   }
376 
377   return rc;
378 }
379 
ndmconn_auth_none(struct ndmconn * conn)380 int ndmconn_auth_none(struct ndmconn* conn)
381 {
382   int rc;
383 
384   switch (conn->protocol_version) {
385     default:
386       ndmconn_set_err_msg(conn, "connect-auth-none-vers-botch");
387       return -1;
388 
389 #ifndef NDMOS_OPTION_NO_NDMP2
390     case NDMP2VER:
391       NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
392       request->auth_data.auth_type = NDMP2_AUTH_NONE;
393       rc = NDMC_CALL(conn);
394       NDMC_ENDWITH
395       break;
396 #endif /* !NDMOS_OPTION_NO_NDMP2 */
397 
398 #ifndef NDMOS_OPTION_NO_NDMP3
399     case NDMP3VER:
400       NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
401       request->auth_data.auth_type = NDMP3_AUTH_NONE;
402       rc = NDMC_CALL(conn);
403       NDMC_ENDWITH
404       break;
405 #endif /* !NDMOS_OPTION_NO_NDMP3 */
406 
407 #ifndef NDMOS_OPTION_NO_NDMP4
408     case NDMP4VER:
409       NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
410       request->auth_data.auth_type = NDMP4_AUTH_NONE;
411       rc = NDMC_CALL(conn);
412       NDMC_ENDWITH
413       break;
414 #endif /* !NDMOS_OPTION_NO_NDMP4 */
415   }
416 
417   if (rc) {
418     ndmconn_set_err_msg(conn, "connect-auth-none-failed");
419     return -1;
420   }
421 
422   return 0;
423 }
424 
ndmconn_auth_text(struct ndmconn * conn,char * id,char * pw)425 int ndmconn_auth_text(struct ndmconn* conn, char* id, char* pw)
426 {
427   int rc;
428 
429   switch (conn->protocol_version) {
430     default:
431       ndmconn_set_err_msg(conn, "connect-auth-text-vers-botch");
432       return -1;
433 
434 #ifndef NDMOS_OPTION_NO_NDMP2
435     case NDMP2VER:
436       NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
437       struct ndmp2_auth_text* at;
438 
439       request->auth_data.auth_type = NDMP2_AUTH_TEXT;
440       at = &request->auth_data.ndmp2_auth_data_u.auth_text;
441       at->auth_id = id;
442       at->auth_password = pw;
443       rc = NDMC_CALL(conn);
444       NDMC_ENDWITH
445       break;
446 #endif /* !NDMOS_OPTION_NO_NDMP2 */
447 
448 #ifndef NDMOS_OPTION_NO_NDMP3
449     case NDMP3VER:
450       NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
451       struct ndmp3_auth_text* at;
452 
453       request->auth_data.auth_type = NDMP3_AUTH_TEXT;
454       at = &request->auth_data.ndmp3_auth_data_u.auth_text;
455       at->auth_id = id;
456       at->auth_password = pw;
457       rc = NDMC_CALL(conn);
458       NDMC_ENDWITH
459       break;
460 #endif /* !NDMOS_OPTION_NO_NDMP3 */
461 
462 #ifndef NDMOS_OPTION_NO_NDMP4
463     case NDMP4VER:
464       NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
465       struct ndmp4_auth_text* at;
466 
467       request->auth_data.auth_type = NDMP4_AUTH_TEXT;
468       at = &request->auth_data.ndmp4_auth_data_u.auth_text;
469       at->auth_id = id;
470       at->auth_password = pw;
471       rc = NDMC_CALL(conn);
472       NDMC_ENDWITH
473       break;
474 #endif /* !NDMOS_OPTION_NO_NDMP4 */
475   }
476 
477   if (rc) {
478     ndmconn_set_err_msg(conn, "connect-auth-text-failed");
479     return -1;
480   }
481 
482   return 0;
483 }
484 
ndmconn_auth_md5(struct ndmconn * conn,char * id,char * pw)485 int ndmconn_auth_md5(struct ndmconn* conn, char* id, char* pw)
486 {
487   int rc;
488   char challenge[NDMP_MD5_CHALLENGE_LENGTH];
489   char digest[NDMP_MD5_DIGEST_LENGTH];
490 
491   switch (conn->protocol_version) {
492     default:
493       ndmconn_set_err_msg(conn, "connect-auth-md5-vers-botch");
494       return -1;
495 
496 #ifndef NDMOS_OPTION_NO_NDMP2
497     case NDMP2VER:
498       NDMC_WITH(ndmp2_config_get_auth_attr, NDMP2VER)
499       request->auth_type = NDMP2_AUTH_MD5;
500       rc = NDMC_CALL(conn);
501       if (rc == 0) {
502         if (reply->server_attr.auth_type != NDMP2_AUTH_MD5) {
503           ndmconn_set_err_msg(conn, "connect-auth-md5-attr-type-botch");
504           return -1;
505         }
506         NDMOS_API_BCOPY(reply->server_attr.ndmp2_auth_attr_u.challenge,
507                         challenge, sizeof challenge);
508       }
509       NDMC_ENDWITH
510       break;
511 #endif /* !NDMOS_OPTION_NO_NDMP2 */
512 
513 #ifndef NDMOS_OPTION_NO_NDMP3
514     case NDMP3VER:
515       NDMC_WITH(ndmp3_config_get_auth_attr, NDMP3VER)
516       request->auth_type = NDMP3_AUTH_MD5;
517       rc = NDMC_CALL(conn);
518       if (rc == 0) {
519         if (reply->server_attr.auth_type != NDMP3_AUTH_MD5) {
520           ndmconn_set_err_msg(conn, "connect-auth-md5-attr-type-botch");
521           return -1;
522         }
523         NDMOS_API_BCOPY(reply->server_attr.ndmp3_auth_attr_u.challenge,
524                         challenge, sizeof challenge);
525       }
526       NDMC_ENDWITH
527       break;
528 #endif /* !NDMOS_OPTION_NO_NDMP3 */
529 
530 #ifndef NDMOS_OPTION_NO_NDMP4
531     case NDMP4VER:
532       NDMC_WITH(ndmp4_config_get_auth_attr, NDMP4VER)
533       request->auth_type = NDMP4_AUTH_MD5;
534       rc = NDMC_CALL(conn);
535       if (rc == 0) {
536         if (reply->server_attr.auth_type != NDMP4_AUTH_MD5) {
537           ndmconn_set_err_msg(conn, "connect-auth-md5-attr-type-botch");
538           return -1;
539         }
540         NDMOS_API_BCOPY(reply->server_attr.ndmp4_auth_attr_u.challenge,
541                         challenge, sizeof challenge);
542       }
543       NDMC_ENDWITH
544       break;
545 #endif /* !NDMOS_OPTION_NO_NDMP4 */
546   }
547 
548   if (rc) {
549     ndmconn_set_err_msg(conn, "connect-auth-md5-attr-failed");
550     return -1;
551   }
552 
553   ndmmd5_digest(challenge, pw, digest);
554 
555   switch (conn->protocol_version) {
556     default:
557       ndmconn_set_err_msg(conn, "connect-auth-text-vers-botch");
558       return -1;
559 
560 #ifndef NDMOS_OPTION_NO_NDMP2
561     case NDMP2VER:
562       NDMC_WITH(ndmp2_connect_client_auth, NDMP2VER)
563       struct ndmp2_auth_md5* am;
564 
565       request->auth_data.auth_type = NDMP2_AUTH_MD5;
566       am = &request->auth_data.ndmp2_auth_data_u.auth_md5;
567       am->auth_id = id;
568       NDMOS_API_BCOPY(digest, am->auth_digest, sizeof digest);
569       rc = NDMC_CALL(conn);
570       NDMC_ENDWITH
571       break;
572 #endif /* !NDMOS_OPTION_NO_NDMP2 */
573 
574 #ifndef NDMOS_OPTION_NO_NDMP3
575     case NDMP3VER:
576       NDMC_WITH(ndmp3_connect_client_auth, NDMP3VER)
577       struct ndmp3_auth_md5* am;
578 
579       request->auth_data.auth_type = NDMP3_AUTH_MD5;
580       am = &request->auth_data.ndmp3_auth_data_u.auth_md5;
581       am->auth_id = id;
582       NDMOS_API_BCOPY(digest, am->auth_digest, sizeof digest);
583       rc = NDMC_CALL(conn);
584       NDMC_ENDWITH
585       break;
586 #endif /* !NDMOS_OPTION_NO_NDMP3 */
587 
588 #ifndef NDMOS_OPTION_NO_NDMP4
589     case NDMP4VER:
590       NDMC_WITH(ndmp4_connect_client_auth, NDMP4VER)
591       struct ndmp4_auth_md5* am;
592 
593       request->auth_data.auth_type = NDMP4_AUTH_MD5;
594       am = &request->auth_data.ndmp4_auth_data_u.auth_md5;
595       am->auth_id = id;
596       NDMOS_API_BCOPY(digest, am->auth_digest, sizeof digest);
597       rc = NDMC_CALL(conn);
598       NDMC_ENDWITH
599       break;
600 #endif /* !NDMOS_OPTION_NO_NDMP4 */
601   }
602 
603   if (rc) {
604     ndmconn_set_err_msg(conn, "connect-auth-md5-failed");
605     return -1;
606   }
607 
608   return 0;
609 }
610 
611 
612 /*
613  * CALL (REQUEST/REPLY), SEND, and RECEIVE
614  ****************************************************************
615  */
616 
ndmconn_call(struct ndmconn * conn,struct ndmp_xa_buf * xa)617 int ndmconn_call(struct ndmconn* conn, struct ndmp_xa_buf* xa)
618 {
619   unsigned protocol_version = conn->protocol_version;
620   unsigned msg = xa->request.header.message;
621   int rc;
622   struct ndmp_xdr_message_table* xmte;
623 
624   conn->last_message = msg;
625   conn->last_call_status = NDMCONN_CALL_STATUS_BOTCH;
626   conn->last_header_error = -1; /* invalid */
627   conn->last_reply_error = -1;  /* invalid */
628 
629   if (protocol_version != xa->request.protocol_version) {
630     ndmconn_set_err_msg(conn, "protocol-version-mismatch");
631     return NDMCONN_CALL_STATUS_BOTCH;
632   }
633 
634   xmte = ndmp_xmt_lookup(protocol_version, msg);
635   if (!xmte) {
636     ndmconn_set_err_msg(conn, "no-xdr-found");
637     return NDMCONN_CALL_STATUS_BOTCH;
638   }
639 
640   xa->request.header.message_type = NDMP0_MESSAGE_REQUEST;
641 
642   if (!xmte->xdr_reply) {
643     /* no reply expected, just a send (eg NOTIFY) */
644     return ndmconn_send_nmb(conn, &xa->request);
645   }
646 
647   rc = ndmconn_exchange_nmb(conn, &xa->request, &xa->reply);
648   if (rc) {
649     ndmconn_set_err_msg(conn, "exchange-failed");
650     return NDMCONN_CALL_STATUS_BOTCH;
651   }
652 
653   if (xa->reply.header.message != msg) {
654     ndmconn_set_err_msg(conn, "msg-mismatch");
655     return NDMCONN_CALL_STATUS_BOTCH;
656   }
657 
658   /* TODO: this should be converted ndmp_xto9_error(....) */
659   conn->last_header_error = xa->reply.header.error;
660 
661   if (xa->reply.header.error) {
662     conn->last_call_status = NDMCONN_CALL_STATUS_HDR_ERROR;
663     ndmconn_set_err_msg(conn, "reply-error-hdr");
664     return NDMCONN_CALL_STATUS_HDR_ERROR;
665   }
666 
667   conn->last_reply_error = ndmnmb_get_reply_error(&xa->reply);
668 
669   switch (conn->last_reply_error) {
670     case NDMP9_NO_ERR:
671     case NDMP9_DEV_NOT_OPEN_ERR:
672       /*
673        * We allow NDMP9_DEV_NOT_OPEN_ERR as answer to NDMP4_TAPE_GET_STATE
674        * This always happens during ndmca_monitor_shutdown()
675        */
676       break;
677     default:
678       conn->last_call_status = NDMCONN_CALL_STATUS_REPLY_ERROR;
679       ndmconn_set_err_msg(conn, "reply-error");
680       return NDMCONN_CALL_STATUS_REPLY_ERROR;
681   }
682 
683   return NDMCONN_CALL_STATUS_OK;
684 }
685 
ndmconn_exchange_nmb(struct ndmconn * conn,struct ndmp_msg_buf * request_nmb,struct ndmp_msg_buf * reply_nmb)686 int ndmconn_exchange_nmb(struct ndmconn* conn,
687                          struct ndmp_msg_buf* request_nmb,
688                          struct ndmp_msg_buf* reply_nmb)
689 {
690   int rc;
691 
692   if ((rc = ndmconn_send_nmb(conn, request_nmb)) != 0) return rc;
693   conn->received_time = 0;
694   conn->sent_time = time(0);
695 
696   for (;;) {
697     if ((rc = ndmconn_recv_nmb(conn, reply_nmb)) != 0) return rc;
698 
699     if (reply_nmb->header.message_type == NDMP0_MESSAGE_REPLY &&
700         reply_nmb->header.reply_sequence == request_nmb->header.sequence) {
701       conn->received_time = time(0);
702       return 0;
703     }
704 
705     (*conn->unexpected)(conn, reply_nmb);
706   }
707 }
708 
ndmconn_send_nmb(struct ndmconn * conn,struct ndmp_msg_buf * nmb)709 int ndmconn_send_nmb(struct ndmconn* conn, struct ndmp_msg_buf* nmb)
710 {
711   return ndmconn_xdr_nmb(conn, nmb, XDR_ENCODE);
712 }
713 
ndmconn_recv_nmb(struct ndmconn * conn,struct ndmp_msg_buf * nmb)714 int ndmconn_recv_nmb(struct ndmconn* conn, struct ndmp_msg_buf* nmb)
715 {
716   NDMOS_MACRO_ZEROFILL(nmb);
717   nmb->protocol_version = conn->protocol_version;
718 
719   return ndmconn_xdr_nmb(conn, nmb, XDR_DECODE);
720 }
721 
ndmconn_free_nmb(struct ndmconn * conn,struct ndmp_msg_buf * nmb)722 void ndmconn_free_nmb(struct ndmconn* conn, struct ndmp_msg_buf* nmb)
723 {
724   ndmnmb_free(nmb);
725 }
726 
727 
ndmconn_xdr_nmb(struct ndmconn * conn,struct ndmp_msg_buf * nmb,enum xdr_op x_op)728 int ndmconn_xdr_nmb(struct ndmconn* conn,
729                     struct ndmp_msg_buf* nmb,
730                     enum xdr_op x_op)
731 {
732   xdrproc_t xdr_body = 0;
733 
734   assert(conn->conn_type == NDMCONN_TYPE_REMOTE);
735 
736   if (conn->chan.fd < 0) { return ndmconn_set_err_msg(conn, "not-open"); }
737 
738   conn->xdrs.x_op = x_op;
739 
740   if (x_op == XDR_ENCODE) {
741     xdr_body = ndmnmb_find_xdrproc(nmb);
742 
743     if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) {
744       return ndmconn_set_err_msg(conn, "unknown-body");
745     }
746     nmb->header.sequence = conn->next_sequence++;
747     nmb->header.time_stamp = time(0);
748     ndmconn_snoop_nmb(conn, nmb, "Send");
749   }
750   if (x_op == XDR_DECODE) {
751     if (!xdrrec_skiprecord(&conn->xdrs)) {
752       return ndmconn_set_err_msg(conn, "xdr-get-next");
753     }
754   }
755 
756   if (!xdr_ndmp0_header(&conn->xdrs, &nmb->header)) {
757     ndmconn_abort(conn);
758     if (x_op == XDR_DECODE && conn->chan.eof && !conn->chan.error) {
759       return ndmconn_set_err_msg(conn, "EOF");
760     } else {
761       return ndmconn_set_err_msg(conn, "xdr-hdr");
762     }
763   }
764 
765   if (x_op == XDR_DECODE) {
766     xdr_body = ndmnmb_find_xdrproc(nmb);
767 
768     if (nmb->header.error == NDMP0_NO_ERR && !xdr_body) {
769       return ndmconn_set_err_msg(conn, "unknown-body");
770     }
771   }
772   if (nmb->header.error == NDMP0_NO_ERR) {
773     if (!(*xdr_body)(&conn->xdrs, &nmb->body, 0)) {
774       ndmconn_abort(conn);
775       return ndmconn_set_err_msg(conn, "xdr-body");
776     }
777   }
778 
779   if (x_op == XDR_ENCODE) {
780     if (!xdrrec_endofrecord(&conn->xdrs, 1)) {
781       ndmconn_abort(conn);
782       return ndmconn_set_err_msg(conn, "xdr-send");
783     }
784   }
785   if (x_op == XDR_DECODE) { ndmconn_snoop_nmb(conn, nmb, "Recv"); }
786 
787   return 0;
788 }
789 
790 
791 /*
792  * XDR READ/WRITE CALLBACKS
793  ****************************************************************
794  *
795  * ndmconn_readit() and ndmconn_writeit() are the XDR callbacks
796  * used by xdrrec_create(). They are fundamentally wrappers
797  * around read() and write(), and have very similar parameters.
798  * See the xdr(3) manual page (or try "man xdrrec_create").
799  *
800  * ndmconn_readit() tracks the XDR record marks, and never
801  * reads across a record boundary. This keeps select() an
802  * indicator of when there is a (single) request pending.
803  * Otherwise, we have to check buffers internal to XDR
804  * as well as the file descriptor (via select) to determine
805  * if a request is pending.
806  */
807 
ndmconn_readit(void * a_conn,char * buf,int len)808 int ndmconn_readit(void* a_conn, char* buf, int len)
809 {
810   struct ndmconn* conn = (struct ndmconn*)a_conn;
811   int rc, i, c;
812 
813   /* could impose timeout here */
814   if (conn->chan.fd < 0 || conn->chan.eof) return -1;
815 
816   ndmconn_snoop(conn, 8, "frag_resid=%d fhb_off=%d", conn->frag_resid,
817                 conn->fhb_off);
818 
819   if (conn->frag_resid == 0) {
820     i = 0;
821     while (i < 4) {
822       c = 4 - i;
823 
824       rc = ndmconn_sys_read(conn, (void*)(conn->frag_hdr_buf + i), c);
825       if (rc <= 0) { return rc; }
826       i += rc;
827     }
828     conn->frag_resid = conn->frag_hdr_buf[0] << 24;
829     conn->frag_resid |= conn->frag_hdr_buf[1] << 16;
830     conn->frag_resid |= conn->frag_hdr_buf[2] << 8;
831     conn->frag_resid |= conn->frag_hdr_buf[3];
832     conn->frag_resid &= 0xFFFFFF;
833     conn->fhb_off = 0;
834   }
835   if (conn->fhb_off < 4) {
836     i = 0;
837     while (conn->fhb_off < 4 && len > 0) {
838       buf[i++] = conn->frag_hdr_buf[conn->fhb_off++];
839       len--;
840     }
841     return i;
842   }
843 
844   if ((unsigned int)len > conn->frag_resid)
845     len = (unsigned int)conn->frag_resid;
846 
847   rc = ndmconn_sys_read(conn, buf, len);
848 
849   if (rc > 0) { conn->frag_resid -= rc; }
850 
851   return rc;
852 }
853 
ndmconn_writeit(void * a_conn,char * buf,int len)854 int ndmconn_writeit(void* a_conn, char* buf, int len)
855 {
856   struct ndmconn* conn = (struct ndmconn*)a_conn;
857 
858   /* could impose timeout here */
859   if (conn->chan.fd < 0) return -1;
860 
861   return ndmconn_sys_write(conn, buf, len);
862 }
863 
864 /*
865  * ndmconn_sys_read() and ndmconn_sys_write() are simply
866  * wrappers around read() and write(). They implement
867  * the low-level snooping.
868  */
869 
ndmconn_sys_read(struct ndmconn * conn,char * buf,unsigned len)870 int ndmconn_sys_read(struct ndmconn* conn, char* buf, unsigned len)
871 {
872   int rc;
873 
874   ndmconn_snoop(conn, 9, "reading %d ...", len);
875 
876   rc = read(conn->chan.fd, buf, len);
877 
878   ndmconn_snoop(conn, 8, "read=%d len=%d", rc, len);
879 
880   if (rc <= 0) {
881     conn->chan.eof = 1;
882     if (rc < 0) conn->chan.error = 1;
883   } else {
884     ndmconn_hex_dump(conn, buf, rc);
885   }
886 
887   return rc;
888 }
889 
ndmconn_sys_write(struct ndmconn * conn,char * buf,unsigned len)890 int ndmconn_sys_write(struct ndmconn* conn, char* buf, unsigned len)
891 {
892   int rc;
893 
894   ndmconn_snoop(conn, 9, "writing %d ...", len);
895   ndmconn_hex_dump(conn, buf, len);
896 
897   rc = write(conn->chan.fd, buf, len);
898 
899   ndmconn_snoop(conn, 8, "write=%d len=%d", rc, len);
900 
901   if (rc != (int)len) {
902     conn->chan.eof = 1;
903     conn->chan.error = 1;
904   }
905 
906   return rc;
907 }
908 
909 
910 /*
911  * UNEXPECTED
912  ****************************************************************
913  *
914  * The default unexpected() handler for a connection. It is
915  * called when ndmconn_exchange_nmb() receives something
916  * other than the reply for which it is waiting.
917  * This default routine silently dumps the message.
918  */
919 
ndmconn_unexpected(struct ndmconn * conn,struct ndmp_msg_buf * nmb)920 void ndmconn_unexpected(struct ndmconn* conn, struct ndmp_msg_buf* nmb)
921 {
922   xdrproc_t xdr_body = ndmnmb_find_xdrproc(nmb);
923 
924   if (xdr_body) { xdr_free(xdr_body, (void*)&nmb->body); }
925 }
926 
927 
928 /*
929  * SNOOP
930  ****************************************************************
931  *
932  * The ndmconn snoop stuff. The cool part. This pretty prints
933  * NDMP messages as they go flying by this end-point.
934  */
935 
ndmconn_set_snoop(struct ndmconn * conn,struct ndmlog * log,int level)936 int ndmconn_set_snoop(struct ndmconn* conn, struct ndmlog* log, int level)
937 {
938   conn->snoop_log = log;
939   conn->snoop_level = level;
940   return 0;
941 }
942 
ndmconn_clear_snoop(struct ndmconn * conn)943 void ndmconn_clear_snoop(struct ndmconn* conn)
944 {
945   conn->snoop_log = 0;
946   conn->snoop_level = 0;
947 }
948 
ndmconn_snoop_nmb(struct ndmconn * conn,struct ndmp_msg_buf * nmb,char * whence)949 void ndmconn_snoop_nmb(struct ndmconn* conn,
950                        struct ndmp_msg_buf* nmb,
951                        char* whence)
952 {
953   if (!conn->snoop_log) { return; }
954 
955   ndmnmb_snoop(conn->snoop_log, conn->chan.name, conn->snoop_level, nmb,
956                whence);
957 }
958 
ndmconn_snoop(struct ndmconn * conn,int level,char * fmt,...)959 void ndmconn_snoop(struct ndmconn* conn, int level, char* fmt, ...)
960 {
961   va_list ap;
962 
963   if (conn->snoop_log && conn->snoop_level >= level) {
964     va_start(ap, fmt);
965     ndmlogfv(conn->snoop_log, conn->chan.name, level, fmt, ap);
966     va_end(ap);
967   }
968 }
969 
970 /* used by ndmconn_sys_read() and ndmconn_sys_write() to show low-level */
ndmconn_hex_dump(struct ndmconn * conn,char * buf,unsigned len)971 void ndmconn_hex_dump(struct ndmconn* conn, char* buf, unsigned len)
972 {
973   struct ndmlog* log = conn->snoop_log;
974   char* tag = conn->chan.name;
975   char linebuf[16 * 3 + 3];
976   char* p = linebuf;
977   int b;
978   unsigned i;
979 
980   if (log && conn->snoop_level > 8) {
981     for (i = 0; i < len; i++) {
982       b = buf[i] & 0xFF;
983       sprintf(p, " %02x", b);
984       while (*p) p++;
985       if ((i & 0xF) == 0xF) {
986         ndmlogf(log, tag, 9, "%s", linebuf + 1);
987         p = linebuf;
988       }
989     }
990     if (p > linebuf) { ndmlogf(log, tag, 9, "%s", linebuf + 1); }
991   }
992 }
993 
994 
995 /*
996  * ERRORS
997  ****************************************************************
998  *
999  * Possible errors for ndmconn are not enumerated.
1000  * Instead, errors are indicated by a -1 return, and
1001  * a simple string error message is available for details.
1002  * Appologies for the english-centric design, but it
1003  * is quick and easy, and better than using printf().
1004  */
1005 
ndmconn_set_err_msg(struct ndmconn * conn,char * err_msg)1006 int ndmconn_set_err_msg(struct ndmconn* conn, char* err_msg)
1007 {
1008   conn->last_err_msg = err_msg;
1009   ndmconn_snoop(conn, 4, "ERR=%s", err_msg);
1010   return -1;
1011 }
1012 
ndmconn_get_err_msg(struct ndmconn * conn)1013 char* ndmconn_get_err_msg(struct ndmconn* conn)
1014 {
1015   if (!conn->last_err_msg)
1016     return "-no-error-";
1017   else
1018     return conn->last_err_msg;
1019 }
1020