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