4 * Sebastian Hammer, Adam Dickmeiss
7 * Revision 1.4 1995-05-18 13:02:07 quinn
10 * Revision 1.3 1995/05/16 09:37:18 quinn
13 * Revision 1.2 1995/05/02 08:53:09 quinn
14 * Trying in vain to fix comm with ISODE
16 * Revision 1.1 1995/03/30 14:03:17 quinn
17 * Added RFC1006 as separate library
19 * Revision 1.15 1995/03/30 10:54:43 quinn
20 * Fiddling with packet sizes, trying to help ISODE.. :(
22 * Revision 1.14 1995/03/27 08:36:07 quinn
23 * Some work on nonblocking operation in xmosi.c and rfct.c.
24 * Added protocol parameter to cs_create()
26 * Revision 1.13 1995/03/20 11:27:16 quinn
27 * Fixed bug in the _t_rcv stuff
29 * Revision 1.12 1995/03/20 09:54:07 quinn
32 * Revision 1.11 1995/03/20 09:47:16 quinn
33 * Added server-side support to xmosi.c
34 * Fixed possible problems in rfct
37 * Revision 1.10 1995/03/17 19:37:26 quinn
38 * *** empty log message ***
40 * Revision 1.9 1995/03/17 19:28:32 quinn
41 * Working on fixing our mystery-bug.
43 * Revision 1.8 1995/03/14 10:28:38 quinn
44 * Adding server-side support to tcpip.c and fixing bugs in nonblocking I/O
46 * Revision 1.7 1995/03/09 18:42:32 quinn
47 * Fixed another bug in t_rcv
49 * Revision 1.6 1995/03/09 15:22:42 quinn
50 * Fixed two bugs in get/rcv
52 * Revision 1.5 1995/03/07 16:29:46 quinn
55 * Revision 1.4 1995/03/06 16:48:03 quinn
58 * Revision 1.3 1995/03/06 10:54:32 quinn
59 * Server-side functions (t_bind/t_listen/t_accept) seem to work ok, now.
60 * Nonblocking mode needs work (and testing!)
61 * Added makensap to replace function in mosiutil.c.
63 * Revision 1.2 1995/03/03 09:40:36 quinn
64 * Added most of the remaining functionality.
65 * Still need exstensive testing of server-side functions and nonblocking
68 * Revision 1.1 1995/03/01 08:40:33 quinn
69 * First working version of rfct. Addressing needs work.
74 * Simple implementation of XTI/TP0/RFC1006/Sockets.
75 * Note: This is neither complete nor robust. It just has to tick us over
76 * until mr. Furniss finishes his own implementation.
78 * TODO: Asynchronous mode needs a lot of little adjustments to various
79 * return values and error codes, etc.
81 * Check if addressing info is returned correctly by all calls.
86 #include <sys/types.h>
87 #include <sys/param.h>
88 #include <sys/socket.h>
90 #include <netinet/in.h>
91 #include <arpa/inet.h>
103 #include <linux/limits.h>
105 #include <dmalloc.h> /* project memory debugging - delete if you don't have it */
107 #ifdef TRACE_TRANSPORT
113 #define TSEL_MAXLEN 20 /* is there a standard for this? */
114 #define RFC_DEFAULT_PORT 4500
115 #define MAX_QLEN 5 /* outstanding connect indications */
117 static int t_look_wait(int fd, int wait);
118 int t_rcvconnect(int fd, struct t_call *call);
123 static struct rfct_control
125 int state; /* state of transport endpoint */
126 int event; /* current event */
127 int tsize; /* max TPDU size */
128 int curcode; /* code of current TPDU */
129 int pending; /* # bytes of user data waiting on line */
130 int eot_flag; /* current incoming TPDU had EOT set */
131 int hlen; /* TPDU header suffix remaining on line */
132 int eot; /* current incoming DATA TPDU is last in TSDU */
133 int togo; /* # bytes waiting to go on current outgoing DATA */
134 int qlen; /* set by t_bind */
135 int listen; /* we are listening */
136 int tmpfd; /* holding space for client between listen and accept */
137 int flags; /* set by t_open */
138 char rref[2]; /* remote reference */
139 char ltsel[TSEL_MAXLEN]; /* local transport selector */
141 int oci[MAX_QLEN]; /* outstanding connect indications */
145 * In the best tradition of ThinOSI, we combine the RFC1006 and the common
146 * part of the TPDU header into one. Given the complexity of the transport
147 * layer, maybe it would have been nice to have it in a separate module
148 * for reuse - but such is hindsight.
149 * Using a bit-field like this may be dangerous. I would expect it to work
150 * fine on any 32-bit or 64-bit machine, with any decent compiler. A DOS
151 * compiler might make a mess of it, I suppose, but I wouldn't even expect
158 #define RFC_VERSION 3
160 unsigned len:16; /* length of entire package, including header */
161 /* TPDU common fields */
162 unsigned char hlen; /* length of TPDU-header minus length-octet */
163 unsigned char code; /* TPDU code (and an unused 4-bit field) */
164 char suffix[100]; /* unstructured TPDU elements, for convenience */
168 * The TPDU-codes used by this package.
170 #define TPDU_CODE_CREQ 0xe0
171 #define TPDU_CODE_CCON 0xd0
172 #define TPDU_CODE_DATA 0xf0
173 #define TPDU_CODE_DREQ 0x80
176 * Parameters that we care about.
178 #define TPDU_PARM_TSIZE 0xc0 /* max TPDU size */
179 #define TPDU_PARM_CLGID 0xc1 /* Calling TSAP-ID */
180 #define TPDU_PARM_CLDID 0xc2 /* Called TSAP-ID */
182 struct tpdu_connect_header /* length of fixed suffix: 5 octets */
184 char dst_ref[2]; /* 3-4 - not used by TP0 */
185 char src_ref[2]; /* 5-6 - not used by TP0 */
186 char class; /* 7 - always 0 */
189 struct tpdu_disconnect_header /* length of fixed suffix: 5 octets */
196 struct tpdu_data_header /* length of fixed suffix: 1 octet */
198 unsigned char nr; /* we only use bit 7 (1 << 7), to mark EOT */
199 #define DATA_EOT (1<<7)
202 static int rfc_running = 0; /* Have we been initialized? */
204 static void init_rfc(void)
208 for (i = 0; i < NOFILE; i++)
213 int t_open(char *name, int oflag, struct t_info *info)
215 struct rfct_control *cnt;
216 struct protoent *proto;
219 TRC(fprintf(stderr, "T_OPEN\n"));
224 if (!(proto = getprotobyname("tcp")))
226 if ((s = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0)
228 #ifdef NONBLOCKING_OSI
229 if ((oflag & O_NONBLOCK) && fcntl(s, F_SETFL, O_NONBLOCK) < 0)
235 if (!(cnt = control[s] = malloc(sizeof(struct rfct_control))))
237 TRC(perror("malloc()"));
244 cnt->state = T_UNBND;
251 for (i = 0; i < MAX_QLEN; i++)
255 * RFC1006 sets a higher than standard default max TPDU size, but the
256 * Isode seems to like to negotiate it down. We'll keep it here to be
257 * safe. Note that there's no harm in jumping it up. If it's higher
258 * than 2048, t_connect won't try to negotiate.
264 info->addr = TSEL_MAXLEN + sizeof(struct sockaddr_in) + 1;
265 info->options = 1024;
266 info->tsdu = -1; /* is this right? */
270 info->servtype = T_COTS_ORD; /* lets hope our user doesn't
271 try something funny. */
276 int t_connect(int fd, struct t_call *sndcall, struct t_call *rcvcall)
278 struct rfct_control *cnt = control[fd];
279 struct sockaddr_in addr;
281 struct iovec vec[3]; /* RFC1006 header + T-header + parms */
282 struct rfc_header rfc;
283 struct tpdu_connect_header tpdu;
284 unsigned char pbuf[2 + TSEL_MAXLEN + 3]; /* CR parameters */
287 TRC(fprintf(stderr, "T_CONNECT\n"));
288 if (!cnt || cnt->state != T_IDLE)
290 TRC(fprintf(stderr, "TOUTSTATE\n"));
294 /* take the address apart */
295 p = sndcall->addr.buf;
296 if (*p) /* transport selector */
298 TRC(fprintf(stderr, "Tsel length is %d.\n", *p));
299 pbuf[0] = TPDU_PARM_CLDID;
301 memcpy(pbuf + 2, p + 1, *p);
304 p += *p + 1; /* skip tsel */
305 if (*p != sizeof(addr))
307 TRC(fprintf(stderr, "Expected sockaddr here.\n"));
312 memcpy(&addr, p, sizeof(addr));
313 if (connect(fd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
320 * If the default set by t_open() is higher than 2048, we don't try
321 * to negotiate, hoping that the opponent goes by the high default
322 * set by RFC1006. Otherwise, we just go for 2048 (max according to
323 * transport standard). The rest of this module doesn't really care
324 * about the size (although it's respected, of course), since we do
325 * no internal buffering.
327 if (cnt->tsize <= 2048)
329 pbuf[plen++] = TPDU_PARM_TSIZE;
331 pbuf[plen++] = 0x0b; /* request max PDU size (2048 octets) */
334 rfc.version = RFC_VERSION;
336 rfc.len = htons(4 + 7 + plen);
338 rfc.code = TPDU_CODE_CREQ;
340 memset(tpdu.dst_ref, 0, 2);
341 memset(tpdu.src_ref, 0, 2);
344 vec[0].iov_base = (caddr_t) &rfc;
346 vec[1].iov_base = (caddr_t) &tpdu;
348 vec[2].iov_base = (caddr_t) pbuf;
349 vec[2].iov_len = plen;
352 * we don't expect flow-control problems on the first outgoing packet,
353 * though I suppose it'd be possible on some weird types of link.
355 if (writev(fd, vec, 3) < 4 + 7 + plen)
357 TRC(fprintf(stderr, "writev came up short. Aborting connect\n"));
361 cnt->state = T_OUTCON;
363 return t_rcvconnect(fd, rcvcall);
367 * This needs work for asynchronous mode.
369 static int read_n(int fd, char *buf, int toget)
371 struct rfct_control *cnt = control[fd];
376 if ((res = read(fd, buf, toget - got)) < 0)
382 TRC(fprintf(stderr, "Error on read.\n"));
387 if (!res) /* peer closed network connection */
390 cnt->event = T_DISCONNECT; /* is this correct ? ## */
391 cnt->hlen = cnt->pending = 0;
392 cnt->state = T_IDLE; /* this can't be correct ## */
397 while ((got += res) < toget);
401 int t_rcvconnect(int fd, struct t_call *call)
403 struct rfct_control *cnt = control[fd];
404 struct tpdu_connect_header chead;
405 char buf[100], *p, *addrp;
406 struct sockaddr_in peer;
407 int len = sizeof(struct sockaddr_in);
409 TRC(fprintf(stderr, "T_RCVCONNECT\n"));
410 if (!cnt || cnt->state != T_OUTCON)
412 TRC(fprintf(stderr, "TOUTSTATE\n"));
417 if (t_look_wait(fd, 1) <= 0)
419 if (cnt->event != T_CONNECT)
424 /* read the rest of the CC TPDU */
425 if (read_n(fd, buf, cnt->hlen) <= 0)
427 memcpy(&chead, buf, 5);
428 if (chead.class != 0)
430 TRC(fprintf(stderr, "Expected TP0, got %d\n", (int)chead.class));
435 *(addrp = call->addr.buf) = 0;
437 for (p = buf + 5; cnt->hlen > 0;)
439 switch ((unsigned char)*p)
441 case TPDU_PARM_TSIZE:
442 cnt->tsize = 1 << *(p + 2);
444 case TPDU_PARM_CLDID:
447 if (*(p + 1) > TSEL_MAXLEN)
449 TRC(fprintf(stderr, "Called TSEL too long.\n"));
450 t_errno = TSYSERR; /* Wrong.. ## */
453 *addrp = *(p + 1); /* length */
454 memcpy(addrp + 1, p + 2, *(p + 1)); /* remote TSEL */
455 addrp += *(p + 1); /* move past TSEL */
458 case TPDU_PARM_CLGID: break; /* ignoring this for now */
460 TRC(fprintf(stderr, "Inoring CR parameter: %d\n",
462 /* we silently ignore anything else */
465 cnt->hlen -= (unsigned char) *p + 2;
466 p += (unsigned char) *p + 1;
468 addrp++; /* skip to end of addr + 1 */
471 if (getpeername(fd, (struct sockaddr*) &peer, &len) < 0)
473 TRC(perror("getpeername()"));
477 *(addrp++) = sizeof(struct sockaddr_in);
478 memcpy(addrp, &peer, sizeof(struct sockaddr_in));
479 addrp += sizeof(struct sockaddr_in);
480 call->addr.len = addrp - call->addr.buf + 1;
482 cnt->state = T_DATAXFER;
487 int t_snd(int fd, char *buf, unsigned nbytes, int flags)
489 struct rfct_control *cnt = control[fd];
490 struct rfc_header rfc;
491 struct iovec vec[2]; /* RFC1006 header + T-header + (user data) */
492 int writ = 0, res, towrite, eot;
495 TRC(fprintf(stderr, "T_SND [%d bytes, flags %d]\n", nbytes, flags));
496 if (!cnt || cnt->state != T_DATAXFER)
498 TRC(fprintf(stderr, "Trying to write in the wrong state on fd %d.\n",
508 do /* write the TSDU (segment) in chunks depending on the TPDU max size */
510 if (cnt->togo > 0) /* we have a partial TPDU on the wire */
512 TRC(fprintf(stderr, " writing continuation block (%d)\n",
514 if ((res = write(fd, buf, cnt->togo)) < 0)
526 TRC(fprintf(stderr, " wrote %d, total %d\n", res, writ));
528 else /* prepare and send (possibly partial) header */
530 towrite = nbytes - writ;
531 if (towrite + 3 + 4 > cnt->tsize)
532 towrite = cnt->tsize - (3 + 4); /* space for DATA header */
533 rfc.version = RFC_VERSION;
535 rfc.len = htons(towrite + 4 + 3); /* RFC1006 length */
537 rfc.code = TPDU_CODE_DATA;
538 if (flags & T_MORE || towrite + writ < nbytes)
542 rfc.suffix[0] = eot << 7; /* DATA EOT marker */
544 head_offset = 7 + cnt->togo;
547 vec[0].iov_base = (caddr_t) (char*)&rfc + head_offset;
548 vec[0].iov_len = 7 - head_offset;
549 vec[1].iov_base = (caddr_t) buf + writ;
550 vec[1].iov_len = towrite;
551 TRC(fprintf(stderr, " sending beg of block (%d+%d)\n",
552 7 - head_offset, towrite));
553 if ((res = writev(fd, vec, 2)) < 0)
555 TRC(fprintf(stderr, " write returned -1\n"));
556 /* thwarted by flow control */
571 /* somewhat thwarted */
572 else if (res < towrite + 7 - head_offset)
575 * Write came up short. We assume that this is a flow-
576 * control thing, and return immediately. Maybe it'd
577 * be better to take another loop, and generate an
578 * actual EAGAIN from write?
580 TRC(fprintf(stderr, " write returned %d\n", res));
581 if (res < 7 - head_offset) /* we didn't send a full header */
583 cnt->togo = -(7 - head_offset - res);
587 else if ((res -= 7 - head_offset) < towrite) /* not all data */
589 cnt->togo = towrite - res;
590 return nbytes - (writ + res);
593 else /* whew... nonblocking I/O is hard work */
596 writ += res - (7 - head_offset);
600 while (writ < nbytes);
601 TRC(fprintf(stderr, " finishing with %d written\n", nbytes));
605 int _t_rcv(int fd, char *buf, unsigned nbytes, int *flags)
607 struct rfct_control *cnt = control[fd];
608 struct tpdu_data_header dhead;
613 TRC(fprintf(stderr, "T_RCV [nbytes=%d, flags=0x%x]\n", nbytes, *flags));
614 if (!cnt || cnt->state != T_DATAXFER)
620 do /* loop until we have a full TSDU or an error */
623 if (t_look_wait(fd, 0) <= 0)
625 if (cnt->event != T_DATA)
630 if (cnt->hlen) /* beginning of block */
632 TRC(fprintf(stderr, " Beginning of TPDU\n"));
635 TRC(fprintf(stderr, "We can't handle parameters to DATA\n"));
639 toget = cnt->pending;
640 if (toget > nbytes - got)
641 toget = nbytes - got;
642 TRC(fprintf(stderr, " toget=%d\n", toget));
643 vec[0].iov_base = (caddr_t) &dhead;
645 vec[1].iov_base = (caddr_t) buf + got;
646 vec[1].iov_len = toget;
647 if ((res = readv(fd, vec, 2)) < 0)
649 TRC(perror("readv()"));
652 if (got) /* we got some data in previous cycle */
654 t_errno = TNODATA; /* no data */
660 TRC(fprintf(stderr, " readv() returned %d\n", res));
664 cnt->event = T_DISCONNECT;
668 cnt->eot_flag = (dhead.nr & DATA_EOT) >> 7;
671 TRC(fprintf(stderr, " Got total of %d octets, %d pending\n",
674 else /* continuation */
676 TRC(fprintf(stderr, " Reading middle of TPDU\n"));
677 toget = cnt->pending;
678 if (toget > nbytes - got)
679 toget = nbytes - got;
680 TRC(fprintf(stderr, " toget=%d\n", toget));
681 if ((res = read(fd, buf + got, toget)) < 0)
683 TRC(perror("read()"));
686 if (got) /* we got some data in previous cycle */
688 t_errno = TNODATA; /* no data */
694 TRC(fprintf(stderr, " read() returned %d\n", res));
698 cnt->event = T_DISCONNECT;
703 TRC(fprintf(stderr, " Got total of %d octets, %d pending\n",
706 TRC(fprintf(stderr, " bottom of loop: pending=%d, got=%d\n",
709 while (cnt->pending && got < nbytes);
710 *flags = cnt->pending || !cnt->eot_flag ? T_MORE : 0;
711 TRC(fprintf(stderr, " flags=0x%x\n", *flags));
714 TRC(fprintf(stderr, " Return value: %d\n", got));
715 memset(buf + got, 0, 10);
719 int t_rcv(int fd, char *buf, unsigned nbytes, int *flags)
726 if ((res = _t_rcv(fd, buf, nbytes, flags)) <= 0)
732 while (*flags & T_MORE && nbytes > 0);
738 struct rfct_control *cnt = control[fd];
740 TRC(fprintf(stderr, "T_CLOSE\n"));
741 if (!cnt || cnt->state == T_UNINIT)
743 TRC(fprintf(stderr, "Trying to close a bad fd.\n"));
752 * This isn't right, obviously.
754 int t_error(char *errmsg)
756 TRC(fprintf(stderr, "T_ERROR\n"));
757 fprintf(stderr, "t_error(t_errno=%d):", t_errno);
763 * Put a select in here!!
765 static int t_look_wait(int fd, int wait)
767 struct rfct_control *cnt = control[fd];
768 struct rfc_header head;
771 TRC(fprintf(stderr, "T_LOOK\n"));
772 if (!cnt || cnt->state == T_UNINIT)
779 if (cnt->state == T_IDLE && cnt->tmpfd < 0)
780 return T_LISTEN; /* the only possible type of event */
781 if ((res = read_n(fd, (char*) &head, 6)) < 0)
785 TRC(fprintf(stderr, "Network disconnect\n"));
786 return cnt->event = T_DISCONNECT;
788 TRC(fprintf(stderr, "t_look got %d bytes\n", res));
789 if (head.version != RFC_VERSION)
791 TRC(fprintf(stderr, "Got bad RFC1006 version in t_look: %d.\n",
793 t_errno = TSYSERR; /* should signal protocol error, somehow ## */
796 cnt->curcode = head.code;
797 cnt->hlen = head.hlen - 1; /* length of header suffix */
798 cnt->pending = ntohs(head.len) - 6 - cnt->hlen;
799 TRC(fprintf(stderr, "t_look: len=%d, code=0x%2.2x, hlen=%d.\n",
800 cnt->pending + 6, cnt->curcode, cnt->hlen));
801 switch (cnt->curcode)
803 case TPDU_CODE_CREQ: cnt->event = T_LISTEN; break;
804 case TPDU_CODE_CCON: cnt->event = T_CONNECT; break;
805 case TPDU_CODE_DATA: cnt->event = T_DATA; break;
806 case TPDU_CODE_DREQ: cnt->event = T_DISCONNECT; break;
808 TRC(fprintf(stderr, "t_look: Bad package: 0x%2.2x.\n", cnt->curcode));
809 t_errno = TSYSERR; /* protocol error */
817 return t_look_wait(fd, 0);
821 * If the user doesn't provide a NSAP, and qlen > 0, we'll do a default
822 * server bind. If qlen==0, we won't bind at all - connect will do that
825 int t_bind(int fd, struct t_bind *req, struct t_bind *ret)
827 struct rfct_control *cnt = control[fd];
828 struct sockaddr_in addr;
832 TRC(fprintf(stderr, "T_BIND\n"));
833 if (!cnt || cnt->state != T_UNBND)
835 TRC(fprintf(stderr, "Bad state\n"));
842 cnt->qlen = req->qlen < MAX_QLEN ? req->qlen : MAX_QLEN;
846 if (*p > TSEL_MAXLEN)
848 TRC(fprintf(stderr, "Tsel too large.\n"));
854 memcpy(cnt->ltsel, p + 1, cnt->ltsel_len);
855 p += cnt->ltsel_len + 1;
856 if (*p < sizeof(addr))
858 TRC(fprintf(stderr, "W: No NSAP provided for local bind\n"));
862 memcpy(&addr, p + 1, sizeof(addr));
869 if (!got_addr) /* user didn't give an address - local bind */
871 addr.sin_family = AF_INET;
872 addr.sin_addr.s_addr = INADDR_ANY;
874 addr.sin_port = htons(RFC_DEFAULT_PORT);
875 else /* we'll leave binding to connect - just set dummy */
876 addr.sin_port = 0; /* dummy for ret */
878 if (cnt->qlen && bind(fd, (struct sockaddr *) &addr,
881 TRC(perror("bind()"));
882 t_errno = TSYSERR; /* this should be refined */
887 if (listen(fd, cnt->qlen) < 0)
893 TRC(fprintf(stderr, " listen OK\n"));
896 /* All right! Now let's give something back, if our user wants it */
899 ret->qlen = cnt->qlen;
900 if (ret->addr.maxlen < (ret->addr.len = cnt->ltsel_len + 2 +
903 /* No space - but we're still bound */
909 *(p++) = cnt->ltsel_len;
911 memcpy(p, cnt->ltsel, cnt->ltsel_len);
913 *(p++) = sizeof(addr);
914 memcpy(p, &addr, sizeof(addr));
920 * need to consult RFC1006 on these. I think they just map to close()...
922 int t_snddis(int fd, struct t_call *call)
924 TRC(fprintf(stderr, "T_SNDDIS\n"));
928 int t_rcvdis(int fd, struct t_discon *discon)
930 struct rfct_control *cnt = control[fd];
931 struct tpdu_disconnect_header chead;
932 char udata[64], buf[256], *p;
934 TRC(fprintf(stderr, "T_RCVDIS\n"));
937 TRC(fprintf(stderr, "TOUTSTATE\n"));
942 if (t_look_wait(fd, 1) <= 0)
944 if (cnt->event != T_DISCONNECT)
949 /* read the rest of the DR TPDU */
950 if (read_n(fd, buf, cnt->hlen) <= 0)
952 memcpy(&chead, buf, 5);
954 for (p = buf + 5; cnt->hlen > 0;)
956 switch ((unsigned char)*p)
959 TRC(fprintf(stderr, "Inoring RD parameter: %d\n",
961 /* we silently ignore anything else */
964 cnt->hlen -= (unsigned char) *p + 2;
965 p += (unsigned char) *p + 1;
969 if (read_n(fd, udata, cnt->pending) < cnt->pending)
971 TRC(fprintf(stderr, "Unable to read user data\n"));
976 cnt->state = T_IDLE; /* should we close transport? */
980 discon->sequence = -1; /* TOFIX */
981 discon->reason = chead.reason;
982 TRC(fprintf(stderr, "Diconnect reason %d\n", chead.reason));
983 if (cnt->pending > discon->udata.maxlen)
990 memcpy(discon->udata.buf, udata, cnt->pending);
991 udata[cnt->pending] = '\0';
992 TRC(fprintf(stderr, "Discon udata: '%s'\n", udata));
994 discon->udata.len = cnt->pending;
1000 * fix memory management, you bad Sebastian!
1002 int t_free(char *ptr, int struct_type)
1004 TRC(fprintf(stderr, "T_FREE\n"));
1009 char *t_alloc(int fd, int struct_type, int fields)
1011 char *r = malloc(1024);
1014 TRC(fprintf(stderr, "T_ALLOC\n"));
1020 * this is required for t_listen... if a system doesn't support dup2(), we're
1021 * in trouble: We might not be able to do nonblocking listen. Time will tell.
1023 static int switch_fds(int fd1, int fd2)
1026 struct rfct_control *tmpc;
1028 TRC(fprintf(stderr, "Switching fds %d <--> %d\n", fd1, fd2));
1029 if ((tmp = dup(fd1)) < 0 ||
1030 dup2(fd2, fd1) < 0 ||
1031 dup2(tmp, fd2) < 0 ||
1034 tmpc = control[fd1];
1035 control[fd1] = control[fd2];
1036 control[fd2] = tmpc;
1040 static int rcvconreq(int fd, struct t_call *call)
1042 struct rfct_control *cnt = control[fd];
1043 struct rfct_control *new = control[cnt->tmpfd];
1044 struct tpdu_connect_header chead;
1046 char *p, *addrp = 0;
1047 struct sockaddr_in addr;
1048 int len = sizeof(struct sockaddr_in);
1051 TRC(fprintf(stderr, "RCVCONRES\n"));
1057 for (qslot = 0; qslot < cnt->qlen; qslot++)
1058 if (cnt->oci[qslot] < 0)
1060 if (qslot == cnt->qlen) /* no free slots - shouldn't happen here */
1065 /* read the rest of the CREQ TPDU */
1066 if (read_n(cnt->tmpfd, buf, new->hlen) <= 0)
1068 memcpy(&chead, buf, 5);
1069 if (chead.class != 0)
1071 TRC(fprintf(stderr, "Expected TP0, got %d\n", (int)chead.class));
1075 memcpy(new->rref, chead.src_ref, 2); /* we'll echo this back at her */
1077 if (call && call->addr.maxlen)
1078 *(addrp = call->addr.buf) = 0;
1079 for (p = buf + 5; new->hlen > 0;)
1081 switch ((unsigned char)*p)
1083 case TPDU_PARM_TSIZE:
1084 new->tsize = 1 << *(p + 2); /* we go with their max */
1086 case TPDU_PARM_CLDID: break; /* ignore */
1087 case TPDU_PARM_CLGID:
1090 if (*(p + 1) > TSEL_MAXLEN)
1092 TRC(fprintf(stderr, "Called TSEL too long.\n"));
1093 t_errno = TSYSERR; /* Wrong.. ## */
1096 *addrp = *(p + 1); /* length */
1097 memcpy(addrp + 1, p + 2, *(p + 1)); /* remote TSEL */
1098 addrp += *(p + 1); /* move past TSEL */
1101 /* we silently ignore preferred TPDU size and others */
1104 new->hlen -= (unsigned char) *p + 2;
1105 p += (unsigned char) *p + 1;
1110 if (getpeername(cnt->tmpfd, (struct sockaddr*) &addr, &len) < 0)
1112 TRC(perror("getpeername()"));
1116 *(addrp++) = sizeof(struct sockaddr_in);
1117 memcpy(addrp, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
1121 cnt->oci[qslot] = cnt->tmpfd; /* move the new semi-connection to oci */
1122 call->sequence = qslot;
1124 cnt->state = T_INCON;
1129 * This construction is tricky in async mode: listen calls accept, which
1130 * generates a new fd for the association. Call is supposed to return
1131 * immediately if there is no CR on the line (which is highly likely),
1132 * and t-user will then try to use select() on the wrong socket, waiting for
1133 * the transport level package.
1135 * Compared to the upper-level u_* routines, we have one major handicap
1136 * and one major benefit: We *have* to handle two different endpoints
1137 * after t_listen (which includes sockets accept); and we have access
1140 * If the endpoint is configured for nonblocking I/O, and listen is not
1141 * able to immediately acquire the CR, the two fds are switched, so that
1142 * subsequent selects take place on the data socket, rather than the
1145 * At any rate, the data socket is saved, so that it can later be given
1146 * to the user in t_accept().
1148 int t_listen(int fd, struct t_call *call)
1150 struct rfct_control *cnt = control[fd], *new;
1151 struct sockaddr_in addr;
1152 int addrlen = sizeof(struct sockaddr_in);
1153 int tmpfd_is_new = 0; /* we've just accept()ed a connection */
1156 TRC(fprintf(stderr, "T_LISTEN\n"));
1157 if (!cnt || cnt->state != T_IDLE)
1159 TRC(fprintf(stderr, "T_listen expects state==T_IDLE (wrong?)\n"));
1160 t_errno = TOUTSTATE;
1168 for (i = 0; i < cnt->qlen; i++)
1169 if (cnt->oci[i] < 0)
1171 if (i == cnt->qlen) /* no slots in queue */
1173 TRC(fprintf(stderr, "No more space in queue\n"));
1174 t_errno = TBADF; /* what would be more correct? */
1179 TRC(fprintf(stderr, "Accept..."));
1180 if ((cnt->tmpfd = accept(fd, (struct sockaddr*) &addr, &addrlen)) < 0)
1182 if (errno == EWOULDBLOCK)
1185 TRC(fprintf(stderr, "Accept returned WOULDBLOCK\n"));
1189 TRC(fprintf(stderr, "accept failed\n"));
1194 #ifdef NONBLOCKING_OSI
1195 if ((cnt->flags & O_NONBLOCK) && fcntl(cnt->tmpfd, F_SETFL,
1203 TRC(fprintf(stderr, "Accept OK\n"));
1204 if (!(new = control[cnt->tmpfd] = malloc(sizeof(*new))))
1206 TRC(perror("malloc()"));
1212 new->state = T_IDLE;
1215 new->qlen = cnt->qlen;
1216 new->flags = cnt->flags;
1217 new->tmpfd = cnt->tmpfd;
1219 for (i = 0; i < MAX_QLEN; i++)
1222 /* we got a network connection. Now try to read transport CREQ TPDU */
1223 if ((event = t_look_wait(tmpfd_is_new ? cnt->tmpfd : fd, 1)) < 0)
1225 if (t_errno == TNODATA)
1230 * We give the user something to select on for the incoming
1231 * CR. Switch the new association with the listener socket.
1233 TRC(fprintf(stderr, "Switching FDs\n"));
1234 if (switch_fds(cnt->tmpfd, fd) < 0)
1244 t_close(cnt->tmpfd);
1247 return -1; /* t_look & t_read hopefully set up the right errcodes */
1251 /* We got something! */
1252 if (event != T_LISTEN)
1254 TRC(fprintf(stderr, "Expected T_LISTEN\n"));
1259 * switch back the fds, if necessary */
1260 if (!tmpfd_is_new && switch_fds(fd, cnt->tmpfd) < 0)
1265 if (rcvconreq(fd, call) < 0)
1267 t_close(cnt->tmpfd);
1275 * There's no clean mapping of this onto the socket interface. If someone
1276 * wants it, we could fake it by first closing the socket, and then
1277 * opening a new one and doing a dup2. That should be functionally
1280 int t_unbind(int fd)
1282 fprintf(stderr, "T_UNBIND [not supported by transport implementation]\n");
1283 t_errno = TNOTSUPPORT;
1287 int t_accept(int fd, int resfd, struct t_call *call)
1289 struct rfct_control *listener = control[fd]; /* listener handle */
1290 struct rfct_control *new; /* new, semi-complete association */
1291 struct rfct_control *res; /* resulting association handle */
1292 struct iovec vec[3]; /* RFC1006 header + T-header + parm */
1293 struct rfc_header rfc;
1294 struct tpdu_connect_header tpdu;
1295 unsigned char parm[3 + TSEL_MAXLEN + 2];
1298 TRC(fprintf(stderr, "T_ACCEPT\n"));
1299 if (!listener || listener->state != T_INCON)
1301 TRC(fprintf(stderr, "TOUTSTATE\n"));
1302 t_errno = TOUTSTATE;
1305 /* Get the semi-connection */
1306 if (call->sequence >= listener->qlen || listener->oci[call->sequence] < 0)
1308 TRC(fprintf(stderr, "TBADSEQ\n"));
1312 new = control[(newfd = listener->oci[call->sequence])];
1313 listener->oci[call->sequence] = -1;
1314 res = control[resfd];
1320 if (res != listener) /* move the new connection */
1322 TRC(fprintf(stderr, " Moving to new fd (%d)\n", resfd));
1323 if (res->state != T_IDLE || res->qlen)
1325 TRC(fprintf(stderr, "Trying to move new assc. to bad fd.\n"));
1329 dup2(newfd, resfd); /* closes resfd */
1331 control[resfd] = new;
1332 /* transfer local bindings from res */
1334 memcpy(control[resfd]->ltsel, res->ltsel, res->ltsel_len);
1335 control[resfd]->ltsel_len = res->ltsel_len;
1337 res = control[resfd];
1338 listener->event = 0;
1339 listener->state = T_IDLE;
1341 else /* lose our listener */
1343 TRC(fprintf(stderr, " Moving to listener fd\n"));
1344 for (i = 0; i < listener->qlen; i++)
1345 if (listener->oci[i] >= 0)
1347 TRC(fprintf(stderr, "Still conn indications on listener\n"));
1354 if (listener->ltsel_len)
1355 memcpy(control[resfd]->ltsel, listener->ltsel, listener->ltsel_len);
1356 control[resfd]->ltsel_len = listener->ltsel_len;
1358 res = control[resfd];
1360 rfc.version = RFC_VERSION;
1362 rfc.code = TPDU_CODE_CCON;
1364 memcpy(tpdu.src_ref, "AA", 2);
1365 memcpy(tpdu.dst_ref, res->rref, 2); /* echo back at 'em */
1368 /* grant them their TPDU size */
1369 parm[0] = TPDU_PARM_TSIZE;
1371 for (i = 7; i <= 11 && (1 << i) < res->tsize; i++) ; /* encode TPDU size */
1373 /* give our TSEL. ## Must we echo theirs, if given? check spec */
1374 /* I thought it was ok to give an empty TSEL. Does it have semantic sig? */
1375 parm[3] = TPDU_PARM_CLDID;
1376 parm[4] = res->ltsel_len;
1378 memcpy(parm + 5, res->ltsel, res->ltsel_len);
1380 rfc.len = htons(4 + 7 + 3 + 2 + res->ltsel_len);
1381 rfc.hlen = 6 + 3 + 2 + res->ltsel_len;
1382 vec[0].iov_base = (caddr_t) &rfc;
1384 vec[1].iov_base = (caddr_t) &tpdu;
1386 vec[2].iov_base = (caddr_t) parm;
1387 vec[2].iov_len = 3 + 2 + res->ltsel_len;
1388 if (writev(resfd, vec, 3) < 4 + 7 + 3 + (2 + res->ltsel_len))
1390 TRC(fprintf(stderr, "writev came up short. Aborting connect\n"));
1394 res->state = T_DATAXFER;
1399 int t_getstate(int fd)
1401 TRC(fprintf(stderr, "T_GETSTATE\n"));
1402 return control[fd] ? control[fd]->state : T_UNINIT;