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  * ndmis_connect_status transitions
37  *
38  *   Event relative to  ------ before ------    ------ after -------
39  *   "mine" end point   MINE    PEER    REMO    MINE    PEER    REMO
40  *   ====================================================================
41  *   LISTEN/LOCAL       IDLE    IDLE    IDLE    LISTEN  IDLE    EXCLUDE
42  *   LISTEN/TCP         IDLE    IDLE    IDLE    LISTEN  REMOTE  LISTEN
43  *
44  *   CONNECT/LOCAL      IDLE    LISTEN  EXCLUDE CONN'ED ACC'ED  EXCLUDE
45  *   CONNECT/TCP        IDLE    IDLE    IDLE    CONN'ED REMOTE  CONN'ED
46  *
47  *   tcp_accept()       LISTEN  REMOTE  LISTEN  ACC'ED  REMOTE  ACC'ED
48  *
49  */
50 
51 
52 #if 0
53         DATA                                    TAPE
54         END     ========== LOCAL ============   END
55         POINT                                   POINT
56                         REMOTE
57 #endif
58 
59 
60 #include "ndmagents.h"
61 
62 
ndmis_reinit_remote(struct ndm_session * sess)63 int ndmis_reinit_remote(struct ndm_session* sess)
64 {
65   struct ndm_image_stream* is = sess->plumb.image_stream;
66   struct ndm_tape_agent* ta = sess->tape_acb;
67 
68   NDMOS_MACRO_ZEROFILL(&is->remote);
69 
70   ndmchan_initialize(&is->remote.listen_chan, "image-stream-listen");
71   ndmchan_initialize(&is->remote.sanity_chan, "image-stream-sanity");
72   ndmchan_initialize(&is->chan, "image-stream");
73   if (!is->buf) {
74     is->buflen = ta->mover_state.record_size;
75     is->buf = NDMOS_API_MALLOC(is->buflen);
76     if (!is->buf) { return -1; }
77     NDMOS_MACRO_ZEROFILL_SIZE(is->buf, is->buflen);
78   }
79   ndmchan_setbuf(&is->chan, is->buf, is->buflen);
80 
81   return 0;
82 }
83 
84 
85 /*
86  * Initialization and Cleanup
87  ****************************************************************
88  */
89 
90 /* Initialize -- Set data structure to know value, ignore current value */
ndmis_initialize(struct ndm_session * sess)91 int ndmis_initialize(struct ndm_session* sess)
92 {
93   sess->plumb.image_stream = NDMOS_API_MALLOC(sizeof(struct ndm_image_stream));
94   if (!sess->plumb.image_stream) return -1;
95   NDMOS_MACRO_ZEROFILL(sess->plumb.image_stream);
96   NDMOS_MACRO_ZEROFILL(&sess->plumb.image_stream->chan);
97 
98   ndmis_reinit_remote(sess);
99 
100   sess->plumb.image_stream->data_ep.name = "DATA";
101   sess->plumb.image_stream->tape_ep.name = "TAPE";
102 
103   return 0;
104 }
105 
106 /* Commission -- Get agent ready. Entire session has been initialize()d */
ndmis_commission(struct ndm_session * sess)107 int ndmis_commission(struct ndm_session* sess) { return 0; }
108 
109 /* Decommission -- Discard agent */
ndmis_decommission(struct ndm_session * sess)110 int ndmis_decommission(struct ndm_session* sess) { return 0; }
111 
112 /* Destroy -- Destroy agent */
ndmis_destroy(struct ndm_session * sess)113 int ndmis_destroy(struct ndm_session* sess)
114 {
115   if (!sess->plumb.image_stream) { return 0; }
116 
117   if (sess->plumb.image_stream->buf) {
118     NDMOS_API_FREE(sess->plumb.image_stream->buf);
119   }
120   NDMOS_API_FREE(sess->plumb.image_stream);
121   sess->plumb.image_stream = NULL;
122 
123   return 0;
124 }
125 
126 /* Belay -- Cancel partially issued activation/start */
ndmis_belay(struct ndm_session * sess)127 int ndmis_belay(struct ndm_session* sess) { return 0; }
128 
129 
130 /*
131  * Semantic actions -- called from ndma_dispatch()
132  ****************************************************************
133  */
134 
ndmis_audit_data_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)135 ndmp9_error ndmis_audit_data_listen(struct ndm_session* sess,
136                                     ndmp9_addr_type addr_type,
137                                     char* reason)
138 {
139   struct ndm_image_stream* is;
140   struct ndmis_end_point* mine_ep;
141   struct ndmis_end_point* peer_ep;
142 
143   /*
144    * We are about to start using an Image Stream so allocate it.
145    * Only do this when not allocated yet.
146    */
147   if (!sess->plumb.image_stream) {
148     if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
149   }
150 
151   is = sess->plumb.image_stream;
152   mine_ep = &is->data_ep;
153   peer_ep = &is->tape_ep;
154 
155   return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
156 }
157 
ndmis_audit_tape_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)158 ndmp9_error ndmis_audit_tape_listen(struct ndm_session* sess,
159                                     ndmp9_addr_type addr_type,
160                                     char* reason)
161 {
162   struct ndm_image_stream* is;
163   struct ndmis_end_point* mine_ep;
164   struct ndmis_end_point* peer_ep;
165 
166   /*
167    * We are about to start using an Image Stream so allocate it.
168    * Only do this when not allocated yet.
169    */
170   if (!sess->plumb.image_stream) {
171     if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
172   }
173 
174   is = sess->plumb.image_stream;
175   mine_ep = &is->tape_ep;
176   peer_ep = &is->data_ep;
177 
178   return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
179 }
180 
ndmis_audit_data_connect(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)181 ndmp9_error ndmis_audit_data_connect(struct ndm_session* sess,
182                                      ndmp9_addr_type addr_type,
183                                      char* reason)
184 {
185   struct ndm_image_stream* is;
186   struct ndmis_end_point* mine_ep;
187   struct ndmis_end_point* peer_ep;
188 
189   /*
190    * We are about to start using an Image Stream so allocate it.
191    * Only do this when not allocated yet.
192    */
193   if (!sess->plumb.image_stream) {
194     if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
195   }
196 
197   is = sess->plumb.image_stream;
198   mine_ep = &is->data_ep;
199   peer_ep = &is->tape_ep;
200 
201   return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
202 }
203 
ndmis_audit_tape_connect(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason)204 ndmp9_error ndmis_audit_tape_connect(struct ndm_session* sess,
205                                      ndmp9_addr_type addr_type,
206                                      char* reason)
207 {
208   struct ndm_image_stream* is;
209   struct ndmis_end_point* mine_ep;
210   struct ndmis_end_point* peer_ep;
211 
212   /*
213    * We are about to start using an Image Stream so allocate it.
214    * Only do this when not allocated yet.
215    */
216   if (!sess->plumb.image_stream) {
217     if (ndmis_initialize(sess)) { return NDMP9_NO_MEM_ERR; }
218   }
219 
220   is = sess->plumb.image_stream;
221   mine_ep = &is->tape_ep;
222   peer_ep = &is->data_ep;
223 
224   return ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
225 }
226 
ndmis_data_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,ndmp9_addr * ret_addr,char * reason)227 ndmp9_error ndmis_data_listen(struct ndm_session* sess,
228                               ndmp9_addr_type addr_type,
229                               ndmp9_addr* ret_addr,
230                               char* reason)
231 {
232   struct ndm_image_stream* is = sess->plumb.image_stream;
233   struct ndmis_end_point* mine_ep = &is->data_ep;
234   struct ndmis_end_point* peer_ep = &is->tape_ep;
235 
236   return ndmis_ep_listen(sess, addr_type, ret_addr, reason, mine_ep, peer_ep);
237 }
238 
ndmis_tape_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,ndmp9_addr * ret_addr,char * reason)239 ndmp9_error ndmis_tape_listen(struct ndm_session* sess,
240                               ndmp9_addr_type addr_type,
241                               ndmp9_addr* ret_addr,
242                               char* reason)
243 {
244   struct ndm_image_stream* is = sess->plumb.image_stream;
245   struct ndmis_end_point* mine_ep = &is->tape_ep;
246   struct ndmis_end_point* peer_ep = &is->data_ep;
247 
248   return ndmis_ep_listen(sess, addr_type, ret_addr, reason, mine_ep, peer_ep);
249 }
250 
ndmis_data_connect(struct ndm_session * sess,ndmp9_addr * addr,char * reason)251 ndmp9_error ndmis_data_connect(struct ndm_session* sess,
252                                ndmp9_addr* addr,
253                                char* reason)
254 {
255   struct ndm_image_stream* is = sess->plumb.image_stream;
256   struct ndmis_end_point* mine_ep = &is->data_ep;
257   struct ndmis_end_point* peer_ep = &is->tape_ep;
258   ndmp9_error error;
259 
260   error = ndmis_ep_connect(sess, addr, reason, mine_ep, peer_ep);
261 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
262   if (error == NDMP9_NO_ERR) {
263     if (peer_ep->connect_status == NDMIS_CONN_ACCEPTED &&
264         peer_ep->addr_type == NDMP9_ADDR_LOCAL) {
265       ndmta_quantum(sess);
266     }
267   }
268 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
269   return error;
270 }
271 
ndmis_tape_connect(struct ndm_session * sess,ndmp9_addr * addr,char * reason)272 ndmp9_error ndmis_tape_connect(struct ndm_session* sess,
273                                ndmp9_addr* addr,
274                                char* reason)
275 {
276   struct ndm_image_stream* is = sess->plumb.image_stream;
277   struct ndmis_end_point* mine_ep = &is->tape_ep;
278   struct ndmis_end_point* peer_ep = &is->data_ep;
279 
280   return ndmis_ep_connect(sess, addr, reason, mine_ep, peer_ep);
281 }
282 
ndmis_ep_start(struct ndm_session * sess,int chan_mode,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)283 int ndmis_ep_start(struct ndm_session* sess,
284                    int chan_mode,
285                    struct ndmis_end_point* mine_ep,
286                    struct ndmis_end_point* peer_ep)
287 {
288   struct ndm_image_stream* is = sess->plumb.image_stream;
289 
290   if (mine_ep->connect_status != NDMIS_CONN_CONNECTED &&
291       mine_ep->connect_status != NDMIS_CONN_ACCEPTED) {
292     return -1;
293   }
294 
295   if (mine_ep->transfer_mode != NDMCHAN_MODE_IDLE) { return -2; }
296 
297   if (mine_ep->addr_type == NDMP9_ADDR_LOCAL) {
298     ndmchan_start_resident(&is->chan);
299     if (chan_mode == NDMCHAN_MODE_WRITE) {
300       peer_ep->transfer_mode = NDMCHAN_MODE_READ;
301     } else {
302       peer_ep->transfer_mode = NDMCHAN_MODE_WRITE;
303     }
304   } else if (chan_mode == NDMCHAN_MODE_WRITE) {
305     ndmchan_pending_to_write(&is->chan);
306   } else if (chan_mode == NDMCHAN_MODE_READ) {
307     ndmchan_pending_to_read(&is->chan);
308   } else {
309     return -3;
310   }
311 
312   mine_ep->transfer_mode = chan_mode;
313 
314   return 0;
315 }
316 
ndmis_data_start(struct ndm_session * sess,int chan_mode)317 int ndmis_data_start(struct ndm_session* sess, int chan_mode)
318 {
319   struct ndm_image_stream* is = sess->plumb.image_stream;
320   struct ndmis_end_point* mine_ep = &is->data_ep;
321   struct ndmis_end_point* peer_ep = &is->tape_ep;
322 
323   return ndmis_ep_start(sess, chan_mode, mine_ep, peer_ep);
324 }
325 
ndmis_tape_start(struct ndm_session * sess,int chan_mode)326 int ndmis_tape_start(struct ndm_session* sess, int chan_mode)
327 {
328   struct ndm_image_stream* is = sess->plumb.image_stream;
329   struct ndmis_end_point* mine_ep = &is->tape_ep;
330   struct ndmis_end_point* peer_ep = &is->data_ep;
331 
332   return ndmis_ep_start(sess, chan_mode, mine_ep, peer_ep);
333 }
334 
ndmis_data_close(struct ndm_session * sess)335 int ndmis_data_close(struct ndm_session* sess)
336 {
337   struct ndm_image_stream* is = sess->plumb.image_stream;
338 
339   if (is) {
340     return ndmis_ep_close(sess, &is->data_ep, &is->tape_ep);
341   } else {
342     return 0;
343   }
344 }
345 
ndmis_tape_close(struct ndm_session * sess)346 int ndmis_tape_close(struct ndm_session* sess)
347 {
348   struct ndm_image_stream* is = sess->plumb.image_stream;
349 
350   if (is) {
351     return ndmis_ep_close(sess, &is->tape_ep, &is->data_ep);
352   } else {
353     return 0;
354   }
355 }
356 
357 
358 /*
359  * Quantum -- get a bit of work done
360  ****************************************************************
361  */
362 
ndmis_quantum(struct ndm_session * sess)363 int ndmis_quantum(struct ndm_session* sess)
364 {
365   struct ndm_image_stream* is = sess->plumb.image_stream;
366   struct ndmis_end_point* mine_ep;
367   int rc;
368 
369   if (is->remote.connect_status != NDMIS_CONN_LISTEN)
370     return 0; /* did nothing */
371 
372   if (!is->remote.listen_chan.ready) return 0; /* did nothing */
373 
374   /* now this is going to get hard */
375 
376   if (is->data_ep.connect_status == NDMIS_CONN_LISTEN) {
377     mine_ep = &is->data_ep;
378     /* assert (is->tape_ep.connect_status == NDMIS_CONN_REMOTE); */
379   } else if (is->tape_ep.connect_status == NDMIS_CONN_LISTEN) {
380     mine_ep = &is->tape_ep;
381     /* assert (is->data_ep.connect_status == NDMIS_CONN_REMOTE); */
382   } else {
383     assert(0);
384     return -1;
385   }
386 
387   rc = ndmis_tcp_accept(sess);
388   if (rc == 0) {
389     mine_ep->connect_status = NDMIS_CONN_ACCEPTED;
390     is->remote.connect_status = NDMIS_CONN_ACCEPTED;
391   } else {
392     mine_ep->connect_status = NDMIS_CONN_BOTCHED;
393     is->remote.connect_status = NDMIS_CONN_BOTCHED;
394   }
395 
396   return 1; /* did something */
397 }
398 
399 
400 /*
401  * ndmis_end_point oriented helper routines
402  ****************************************************************
403  */
404 
ndmis_audit_ep_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)405 ndmp9_error ndmis_audit_ep_listen(struct ndm_session* sess,
406                                   ndmp9_addr_type addr_type,
407                                   char* reason,
408                                   struct ndmis_end_point* mine_ep,
409                                   struct ndmis_end_point* peer_ep)
410 {
411   ndmp9_error error = NDMP9_NO_ERR;
412   char* reason_end;
413 
414   sprintf(reason, "IS %s_LISTEN: ", mine_ep->name);
415   reason_end = reason;
416   while (*reason_end) reason_end++;
417 
418   if (mine_ep->connect_status != NDMIS_CONN_IDLE) {
419     sprintf(reason_end, "%s not idle", mine_ep->name);
420     error = NDMP9_ILLEGAL_STATE_ERR;
421     goto out;
422   }
423   if (peer_ep->connect_status != NDMIS_CONN_IDLE) {
424     sprintf(reason_end, "%s not idle", peer_ep->name);
425     error = NDMP9_ILLEGAL_STATE_ERR;
426     goto out;
427   }
428 
429   switch (addr_type) {
430     case NDMP9_ADDR_LOCAL:
431       break;
432 
433     case NDMP9_ADDR_TCP:
434       break;
435 
436     default:
437       strcpy(reason_end, "unknown addr_type");
438       error = NDMP9_ILLEGAL_ARGS_ERR;
439       goto out;
440   }
441 
442 out:
443   if (error == NDMP9_NO_ERR)
444     strcpy(reason_end, "OK");
445   else
446     ndmalogf(sess, 0, 2, "listen %s messy mcs=%d pcs=%d", mine_ep->name,
447              mine_ep->connect_status, peer_ep->connect_status);
448 
449   return error;
450 }
451 
ndmis_audit_ep_connect(struct ndm_session * sess,ndmp9_addr_type addr_type,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)452 ndmp9_error ndmis_audit_ep_connect(struct ndm_session* sess,
453                                    ndmp9_addr_type addr_type,
454                                    char* reason,
455                                    struct ndmis_end_point* mine_ep,
456                                    struct ndmis_end_point* peer_ep)
457 {
458   ndmp9_error error = NDMP9_NO_ERR;
459   char* reason_end;
460 
461   sprintf(reason, "IS %s_CONNECT: ", mine_ep->name);
462   reason_end = reason;
463   while (*reason_end) reason_end++;
464 
465   if (mine_ep->connect_status != NDMIS_CONN_IDLE) {
466     sprintf(reason_end, "%s not idle", mine_ep->name);
467     error = NDMP9_ILLEGAL_STATE_ERR;
468     goto out;
469   }
470 
471   switch (addr_type) {
472     case NDMP9_ADDR_LOCAL:
473       if (peer_ep->connect_status != NDMIS_CONN_LISTEN) {
474         sprintf(reason_end, "LOCAL %s not LISTEN", peer_ep->name);
475         error = NDMP9_ILLEGAL_STATE_ERR;
476         goto out;
477       }
478       if (peer_ep->addr_type != NDMP9_ADDR_LOCAL) {
479         sprintf(reason_end, "LOCAL %s not LOCAL", peer_ep->name);
480         error = NDMP9_ILLEGAL_STATE_ERR;
481         goto out;
482       }
483       break;
484 
485     case NDMP9_ADDR_TCP:
486       if (peer_ep->connect_status != NDMIS_CONN_IDLE) {
487         sprintf(reason_end, "LOCAL %s not IDLE", peer_ep->name);
488         error = NDMP9_ILLEGAL_STATE_ERR;
489         goto out;
490       }
491       break;
492 
493     default:
494       strcpy(reason_end, "unknown addr_type");
495       error = NDMP9_ILLEGAL_ARGS_ERR;
496       goto out;
497   }
498 
499 out:
500   if (error == NDMP9_NO_ERR) strcpy(reason_end, "OK");
501 
502   return error;
503 }
504 
505 
ndmis_ep_listen(struct ndm_session * sess,ndmp9_addr_type addr_type,ndmp9_addr * ret_addr,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)506 ndmp9_error ndmis_ep_listen(struct ndm_session* sess,
507                             ndmp9_addr_type addr_type,
508                             ndmp9_addr* ret_addr,
509                             char* reason,
510                             struct ndmis_end_point* mine_ep,
511                             struct ndmis_end_point* peer_ep)
512 {
513   struct ndm_image_stream* is = sess->plumb.image_stream;
514   char* reason_end;
515   ndmp9_error error;
516 
517   error = ndmis_audit_ep_listen(sess, addr_type, reason, mine_ep, peer_ep);
518   if (error != NDMP9_NO_ERR) return error;
519 
520   reason_end = reason;
521   while (*reason_end && *reason_end != ':') reason_end++;
522   *reason_end++ = ':';
523   *reason_end++ = ' ';
524   *reason_end = 0;
525 
526   NDMOS_MACRO_ZEROFILL(ret_addr);
527   ret_addr->addr_type = addr_type;
528 
529   switch (addr_type) {
530     case NDMP9_ADDR_LOCAL:
531       mine_ep->addr_type = NDMP9_ADDR_LOCAL;
532       mine_ep->connect_status = NDMIS_CONN_LISTEN;
533       is->remote.connect_status = NDMIS_CONN_EXCLUDE;
534       break;
535 
536     case NDMP9_ADDR_TCP:
537       if (ndmis_tcp_listen(sess, ret_addr) != 0) {
538         strcpy(reason_end, "TCP listen() failed");
539         error = NDMP9_CONNECT_ERR;
540         goto out;
541       }
542       mine_ep->addr_type = NDMP9_ADDR_TCP;
543       mine_ep->connect_status = NDMIS_CONN_LISTEN;
544       peer_ep->connect_status = NDMIS_CONN_REMOTE;
545       break;
546 
547     default:
548       reason = "unknown addr_type (bad)";
549       error = NDMP9_ILLEGAL_ARGS_ERR;
550       goto out;
551   }
552 
553 out:
554   if (error == NDMP9_NO_ERR) strcpy(reason_end, "OK");
555 
556   return error;
557 }
558 
ndmis_ep_connect(struct ndm_session * sess,ndmp9_addr * addr,char * reason,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)559 ndmp9_error ndmis_ep_connect(struct ndm_session* sess,
560                              ndmp9_addr* addr,
561                              char* reason,
562                              struct ndmis_end_point* mine_ep,
563                              struct ndmis_end_point* peer_ep)
564 {
565   struct ndm_image_stream* is = sess->plumb.image_stream;
566   ndmp9_addr_type addr_type = addr->addr_type;
567   char* reason_end;
568   ndmp9_error error;
569 
570   error = ndmis_audit_ep_connect(sess, addr_type, reason, mine_ep, peer_ep);
571   if (error != NDMP9_NO_ERR) return error;
572 
573   reason_end = reason;
574   while (*reason_end && *reason_end != ':') reason_end++;
575   *reason_end++ = ':';
576   *reason_end++ = ' ';
577   *reason_end = 0;
578 
579   switch (addr_type) {
580     case NDMP9_ADDR_LOCAL:
581       mine_ep->addr_type = NDMP9_ADDR_LOCAL;
582       mine_ep->connect_status = NDMIS_CONN_CONNECTED;
583       peer_ep->connect_status = NDMIS_CONN_ACCEPTED;
584       is->remote.connect_status = NDMIS_CONN_EXCLUDE;
585       break;
586 
587     case NDMP9_ADDR_TCP:
588       if (ndmis_tcp_connect(sess, addr) != 0) {
589         strcpy(reason_end, "TCP connect() failed");
590         error = NDMP9_CONNECT_ERR;
591         goto out;
592       }
593       mine_ep->addr_type = NDMP9_ADDR_TCP;
594       mine_ep->connect_status = NDMIS_CONN_CONNECTED;
595       peer_ep->connect_status = NDMIS_CONN_REMOTE;
596       break;
597 
598     default:
599       reason = "unknown addr_type (bad)";
600       error = NDMP9_ILLEGAL_ARGS_ERR;
601       goto out;
602   }
603 
604 out:
605   return error;
606 }
607 
ndmis_ep_close(struct ndm_session * sess,struct ndmis_end_point * mine_ep,struct ndmis_end_point * peer_ep)608 int ndmis_ep_close(struct ndm_session* sess,
609                    struct ndmis_end_point* mine_ep,
610                    struct ndmis_end_point* peer_ep)
611 {
612   struct ndm_image_stream* is = sess->plumb.image_stream;
613   char* save_name = mine_ep->name;
614 
615   switch (mine_ep->connect_status) {
616     case NDMIS_CONN_IDLE:
617       return 0;
618 
619     case NDMIS_CONN_BOTCHED:
620     case NDMIS_CONN_REMOTE:
621     case NDMIS_CONN_EXCLUDE:
622       goto messy;
623 
624     case NDMIS_CONN_LISTEN:
625       switch (mine_ep->addr_type) {
626         default:
627           goto messy;
628 
629         case NDMP9_ADDR_LOCAL:
630           ndmis_reinit_remote(sess);
631           if (peer_ep->connect_status != NDMIS_CONN_IDLE) goto messy;
632           break;
633 
634         case NDMP9_ADDR_TCP:
635           ndmis_tcp_close(sess);
636           if (peer_ep->connect_status != NDMIS_CONN_REMOTE) goto messy;
637           peer_ep->connect_status = NDMIS_CONN_IDLE;
638           break;
639       }
640       break;
641 
642     case NDMIS_CONN_ACCEPTED:
643       switch (mine_ep->addr_type) {
644         default:
645           goto messy;
646 
647         case NDMP9_ADDR_LOCAL:
648           if (peer_ep->connect_status != NDMIS_CONN_CONNECTED) goto messy;
649           peer_ep->connect_status = NDMIS_CONN_DISCONNECTED;
650           is->chan.eof = 1;
651           if (mine_ep->transfer_mode == NDMCHAN_MODE_READ)
652             is->chan.error = 1; /* EPIPE */
653           break;
654 
655         case NDMP9_ADDR_TCP:
656           ndmis_tcp_close(sess);
657           if (peer_ep->connect_status != NDMIS_CONN_REMOTE) goto messy;
658           peer_ep->connect_status = NDMIS_CONN_IDLE;
659           break;
660       }
661       break;
662 
663     case NDMIS_CONN_CONNECTED:
664       switch (mine_ep->addr_type) {
665         default:
666           goto messy;
667 
668         case NDMP9_ADDR_LOCAL:
669           if (peer_ep->connect_status != NDMIS_CONN_ACCEPTED) goto messy;
670           peer_ep->connect_status = NDMIS_CONN_DISCONNECTED;
671           is->chan.eof = 1;
672           if (mine_ep->transfer_mode == NDMCHAN_MODE_READ)
673             is->chan.error = 1; /* EPIPE */
674           break;
675 
676         case NDMP9_ADDR_TCP:
677           ndmis_tcp_close(sess);
678           if (peer_ep->connect_status != NDMIS_CONN_REMOTE) goto messy;
679           peer_ep->connect_status = NDMIS_CONN_IDLE;
680           break;
681       }
682       break;
683 
684     case NDMIS_CONN_DISCONNECTED: /* peer close()d first */
685       ndmis_reinit_remote(sess);
686       break;
687 
688     case NDMIS_CONN_CLOSED:
689       goto messy;
690   }
691 
692   NDMOS_MACRO_ZEROFILL(mine_ep);
693   mine_ep->name = save_name;
694 
695   return 0;
696 
697 messy:
698   ndmalogf(sess, 0, 2, "close %s messy mcs=%d pcs=%d", mine_ep->name,
699            mine_ep->connect_status, peer_ep->connect_status);
700   NDMOS_MACRO_ZEROFILL(mine_ep);
701   mine_ep->name = save_name;
702   return -1;
703 }
704 
705 /*
706  * ADDR_TCP helper routines
707  ****************************************************************
708  */
709 
710 
711 /*
712  * ndmis_tcp_listen()
713  *
714  * The tricky part of listen()ing is determining the IP
715  * address to offer, which ultimately will be used by
716  * the other (peer) side for connect()ing.
717  *
718  * We can't just bind() with INADDR_ANY (0's) because
719  * that results in a local socket with INADDR_ANY, and
720  * any inbound connection to the right port will be
721  * accept()ed by the networking system. That doesn't
722  * help us here, though, because we have to have a
723  * real IP address to offer. INADDR_ANY ain't sufficient.
724  *
725  * There is also the issue of systems with multiple
726  * network connections. We of course would like to
727  * use the network data link that is most advantageous
728  * on both sides. This may vary from job run to job
729  * run, and so any method of specifying just one
730  * is IP address ain't sufficient.
731  *
732  * The approach here uses the existing control connections,
733  * which normally precede the image stream connection,
734  * as cues for which IP address to use. So, for example,
735  * if a TAPE or DATA host has four network connections,
736  * the CONTROL agent can coax them to use a specific one
737  * of the four by connecting to the IP address of the
738  * network wanted for the image stream.
739  *
740  * If the clever rules don't work out, the fallback is to
741  * look up the host name. Right now we use ndmhost_lookup()
742  * of sess->local_info.host_name because both must work
743  * before things would progress to this point.
744  */
745 
ndmis_tcp_listen(struct ndm_session * sess,struct ndmp9_addr * listen_addr)746 int ndmis_tcp_listen(struct ndm_session* sess, struct ndmp9_addr* listen_addr)
747 {
748   struct ndm_image_stream* is = sess->plumb.image_stream;
749   ndmp9_tcp_addr* tcp_addr = &listen_addr->ndmp9_addr_u.tcp_addr;
750   struct ndmconn* conn;
751   struct sockaddr c_sa;
752   struct sockaddr l_sa;
753   struct sockaddr_in* sin;
754   socklen_t len;
755   int listen_sock = -1;
756   char* what = "???";
757 
758   /*
759    * Get the IP address thru which the CONTROL agent connected
760    * to this session. The CONTROL agent may influence the
761    * network used for the image-stream on multi-homed hosts
762    * simply by connecting to the prefered IP address.
763    */
764   what = "determine-conn";
765   conn = sess->plumb.control;
766   if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) {
767     /*
768      * If CONTROL is resident, try the other
769      * control connections in hopes of finding
770      * a clue about what IP address to offer.
771      */
772     conn = sess->plumb.data;
773     if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) {
774       conn = sess->plumb.tape;
775       if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) { conn = 0; }
776     }
777   }
778 
779   if (conn) {
780     /*
781      * We found a connection to use for determining
782      * what IP address to offer.
783      */
784     len = sizeof c_sa;
785     if (getsockname(ndmconn_fileno(conn), &c_sa, &len) < 0) {
786       /* we'll try the fallback rules */
787       conn = 0;
788     }
789   }
790 
791   if (!conn) {
792     /*
793      * For whatever reason, we can't determine a good
794      * IP address from the connections. Try the boring
795      * fallback rules.
796      */
797     ndmos_sync_config_info(sess);
798 
799     sin = (struct sockaddr_in*)&c_sa;
800 
801     what = "ndmhost_lookup";
802     if (ndmhost_lookup(sess->config_info->hostname, sin) != 0) goto fail;
803   }
804 
805   /* c_sa is a sockaddr_in for the IP address to use */
806 
807   what = "socket";
808   listen_sock = socket(AF_INET, SOCK_STREAM, 0);
809   if (listen_sock < 0) goto fail;
810 
811   /* could bind() to more restrictive addr based on c_sa */
812   NDMOS_MACRO_SET_SOCKADDR(&l_sa, 0, 0);
813   what = "bind";
814   if (bind(listen_sock, &l_sa, sizeof l_sa) < 0) goto fail;
815 
816   what = "listen";
817   if (listen(listen_sock, 1) < 0) goto fail;
818 
819   ndmos_condition_listen_socket(sess, listen_sock);
820 
821   /* Get the port */
822   what = "getsockname-listen";
823   len = sizeof l_sa;
824   if (getsockname(listen_sock, &l_sa, &len) < 0) goto fail;
825 
826   /*
827    * Fill in the return address
828    */
829 
830   listen_addr->addr_type = NDMP9_ADDR_TCP;
831   tcp_addr = &listen_addr->ndmp9_addr_u.tcp_addr;
832 
833   /* IP addr from CONTROL connection, or where ever c_sa came from */
834   sin = (struct sockaddr_in*)&c_sa;
835   tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
836 
837   /* port from the bind() and getsockname() above */
838   sin = (struct sockaddr_in*)&l_sa;
839   tcp_addr->port = ntohs(sin->sin_port);
840 
841   /*
842    * Start the listen channel
843    */
844 
845   ndmchan_start_listen(&is->remote.listen_chan, listen_sock);
846 
847   is->remote.connect_status = NDMIS_CONN_LISTEN;
848   is->remote.listen_addr = *listen_addr;
849 
850   return 0;
851 
852 fail:
853   ndmalogf(sess, 0, 2, "ndmis_tcp_listen(): %s failed", what);
854   if (listen_sock >= 0) close(listen_sock);
855 
856   return -1;
857 }
858 
ndmis_tcp_accept(struct ndm_session * sess)859 int ndmis_tcp_accept(struct ndm_session* sess)
860 {
861   struct ndm_image_stream* is = sess->plumb.image_stream;
862   char* what = "???";
863   ndmp9_tcp_addr* tcp_addr;
864   struct sockaddr sa;
865   struct sockaddr_in* sin = (struct sockaddr_in*)&sa;
866   socklen_t len;
867   int accept_sock = -1;
868 
869   what = "remote-conn-stat";
870   if (is->remote.connect_status != NDMIS_CONN_LISTEN) goto fail;
871 
872   what = "remote-list-ready";
873   if (!is->remote.listen_chan.ready) goto fail;
874 
875   what = "accept";
876   len = sizeof sa;
877   accept_sock = accept(is->remote.listen_chan.fd, &sa, &len);
878 
879   ndmchan_cleanup(&is->remote.listen_chan);
880 
881   if (accept_sock < 0) {
882     is->remote.connect_status = NDMIS_CONN_BOTCHED;
883     goto fail;
884   }
885 
886   /* write what we know, ndmis...addrs() will update if possible */
887   is->remote.peer_addr.addr_type = NDMP9_ADDR_TCP;
888   tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
889   tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
890   tcp_addr->port = ntohs(sin->sin_port);
891 
892   ndmis_tcp_green_light(sess, accept_sock, NDMIS_CONN_ACCEPTED);
893 
894   return 0;
895 
896 fail:
897   ndmalogf(sess, 0, 2, "ndmis_tcp_accept(): %s failed", what);
898 
899   return -1;
900 }
901 
ndmis_tcp_connect(struct ndm_session * sess,struct ndmp9_addr * connect_addr)902 int ndmis_tcp_connect(struct ndm_session* sess, struct ndmp9_addr* connect_addr)
903 {
904   struct ndm_image_stream* is = sess->plumb.image_stream;
905   ndmp9_tcp_addr* tcp_addr = &connect_addr->ndmp9_addr_u.tcp_addr;
906   char* what = "???";
907   struct sockaddr sa;
908   int connect_sock;
909 
910   NDMOS_MACRO_SET_SOCKADDR(&sa, tcp_addr->ip_addr, tcp_addr->port);
911 
912   what = "socket";
913   connect_sock = socket(AF_INET, SOCK_STREAM, 0);
914   if (connect_sock < 0) goto fail;
915 
916   what = "connect";
917   if (connect(connect_sock, &sa, sizeof sa) < 0) goto fail;
918 
919 
920   /* write what we know, ndmis...addrs() will update if possible */
921   is->remote.peer_addr = *connect_addr;
922 
923   ndmis_tcp_green_light(sess, connect_sock, NDMIS_CONN_CONNECTED);
924 
925   return 0;
926 
927 fail:
928   ndmalogf(sess, 0, 2, "ndmis_tcp_connect(): %s failed", what);
929   if (connect_sock >= 0) close(connect_sock);
930 
931   return -1;
932 }
933 
ndmis_tcp_green_light(struct ndm_session * sess,int sock,ndmis_connect_status new_status)934 int ndmis_tcp_green_light(struct ndm_session* sess,
935                           int sock,
936                           ndmis_connect_status new_status)
937 {
938   struct ndm_image_stream* is = sess->plumb.image_stream;
939 
940   ndmos_condition_image_stream_socket(sess, sock);
941 
942   ndmchan_start_pending(&is->chan, sock);
943 
944   is->remote.connect_status = new_status;
945 
946   ndmis_tcp_get_local_and_peer_addrs(sess);
947 
948   return 0;
949 }
950 
ndmis_tcp_close(struct ndm_session * sess)951 int ndmis_tcp_close(struct ndm_session* sess)
952 {
953   struct ndm_image_stream* is = sess->plumb.image_stream;
954 
955   switch (is->remote.connect_status) {
956     case NDMIS_CONN_LISTEN:
957       ndmchan_cleanup(&is->remote.listen_chan);
958       break;
959 
960     case NDMIS_CONN_CONNECTED:
961     case NDMIS_CONN_ACCEPTED:
962       ndmchan_cleanup(&is->chan);
963       break;
964 
965     default:
966       break;
967   }
968 
969   ndmis_reinit_remote(sess);
970 
971   return 0;
972 }
973 
ndmis_tcp_get_local_and_peer_addrs(struct ndm_session * sess)974 int ndmis_tcp_get_local_and_peer_addrs(struct ndm_session* sess)
975 {
976   struct ndm_image_stream* is = sess->plumb.image_stream;
977   char* what = "???";
978   struct sockaddr sa;
979   struct sockaddr_in* sin = (struct sockaddr_in*)&sa;
980   ndmp9_tcp_addr* tcp_addr;
981   socklen_t len;
982   int rc = 0;
983 
984   len = sizeof sa;
985   what = "getpeername";
986   if (getpeername(is->chan.fd, &sa, &len) < 0) {
987     /* this is best effort */
988     ndmalogf(sess, 0, 2, "ndmis_tcp..._addrs(): %s failed", what);
989     rc = -1;
990   } else {
991     is->remote.peer_addr.addr_type = NDMP9_ADDR_TCP;
992     tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
993     tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
994     tcp_addr->port = ntohs(sin->sin_port);
995   }
996 
997   len = sizeof sa;
998   what = "getsockname";
999   if (getsockname(is->chan.fd, &sa, &len) < 0) {
1000     /* this is best effort */
1001     ndmalogf(sess, 0, 2, "ndmis_tcp..._addrs(): %s failed", what);
1002     rc = -1;
1003   } else {
1004     is->remote.local_addr.addr_type = NDMP9_ADDR_TCP;
1005     tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
1006     tcp_addr->ip_addr = ntohl(sin->sin_addr.s_addr);
1007     tcp_addr->port = ntohs(sin->sin_port);
1008   }
1009 
1010   return rc;
1011 }
1012