c31bce762ad937a09ed856ba1bf997916a5d06f2
[yaz-moved-to-github.git] / rfc1006 / rfct.c
1 /*
2  * 1995, Index Data I/S 
3  * 
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: rfct.c,v $
7  * Revision 1.5  1995-05-31 08:29:35  quinn
8  * Nothing significant.
9  *
10  * Revision 1.4  1995/05/18  13:02:07  quinn
11  * Smallish.
12  *
13  * Revision 1.3  1995/05/16  09:37:18  quinn
14  * Fixed bug
15  *
16  * Revision 1.2  1995/05/02  08:53:09  quinn
17  * Trying in vain to fix comm with ISODE
18  *
19  * Revision 1.1  1995/03/30  14:03:17  quinn
20  * Added RFC1006 as separate library
21  *
22  * Revision 1.15  1995/03/30  10:54:43  quinn
23  * Fiddling with packet sizes, trying to help ISODE.. :(
24  *
25  * Revision 1.14  1995/03/27  08:36:07  quinn
26  * Some work on nonblocking operation in xmosi.c and rfct.c.
27  * Added protocol parameter to cs_create()
28  *
29  * Revision 1.13  1995/03/20  11:27:16  quinn
30  * Fixed bug in the _t_rcv stuff
31  *
32  * Revision 1.12  1995/03/20  09:54:07  quinn
33  * Debugging session
34  *
35  * Revision 1.11  1995/03/20  09:47:16  quinn
36  * Added server-side support to xmosi.c
37  * Fixed possible problems in rfct
38  * Other little mods
39  *
40  * Revision 1.10  1995/03/17  19:37:26  quinn
41  * *** empty log message ***
42  *
43  * Revision 1.9  1995/03/17  19:28:32  quinn
44  * Working on fixing our mystery-bug.
45  *
46  * Revision 1.8  1995/03/14  10:28:38  quinn
47  * Adding server-side support to tcpip.c and fixing bugs in nonblocking I/O
48  *
49  * Revision 1.7  1995/03/09  18:42:32  quinn
50  * Fixed another bug in t_rcv
51  *
52  * Revision 1.6  1995/03/09  15:22:42  quinn
53  * Fixed two bugs in get/rcv
54  *
55  * Revision 1.5  1995/03/07  16:29:46  quinn
56  * Various fixes.
57  *
58  * Revision 1.4  1995/03/06  16:48:03  quinn
59  * Smallish changes.
60  *
61  * Revision 1.3  1995/03/06  10:54:32  quinn
62  * Server-side functions (t_bind/t_listen/t_accept) seem to work ok, now.
63  * Nonblocking mode needs work (and testing!)
64  * Added makensap to replace function in mosiutil.c.
65  *
66  * Revision 1.2  1995/03/03  09:40:36  quinn
67  * Added most of the remaining functionality.
68  * Still need exstensive testing of server-side functions and nonblocking
69  * I/O.
70  *
71  * Revision 1.1  1995/03/01  08:40:33  quinn
72  * First working version of rfct. Addressing needs work.
73  *
74  */
75
76 /*
77  * Simple implementation of XTI/TP0/RFC1006/Sockets.
78  * Note: This is neither complete nor robust. It just has to tick us over
79  * until mr. Furniss finishes his own implementation.
80  *
81  * TODO: Asynchronous mode needs a lot of little adjustments to various
82  * return values and error codes, etc.
83  *
84  * Check if addressing info is returned correctly by all calls.
85  */
86
87 #include <stdio.h>
88 #include <string.h>
89 #include <sys/types.h>
90 #include <sys/param.h>
91 #include <sys/socket.h>
92 #include <netdb.h>
93 #include <netinet/in.h>
94 #include <arpa/inet.h>
95
96 #include <assert.h>
97 #include <stdlib.h>
98 #include <unistd.h>
99 #include <sys/uio.h>
100 #include <errno.h>
101 #include <fcntl.h>
102
103 #include <xti.h>
104 #include <fcntl.h>
105 #ifdef __linux__
106 #include <linux/limits.h>
107 #endif
108 #include <dmalloc.h>  /* project memory debugging - delete if you don't have it */
109
110 #ifdef TRACE_TRANSPORT
111 #define TRC(x) x
112 #else
113 #define TRC(X)
114 #endif
115
116 #define TSEL_MAXLEN 20   /* is there a standard for this? */
117 #define RFC_DEFAULT_PORT 4500
118 #define MAX_QLEN 5        /* outstanding connect indications */
119
120 static int t_look_wait(int fd, int wait);
121 int t_rcvconnect(int fd, struct t_call *call);
122
123 int t_errno = 0;
124 int xti_trace = 0;
125
126 static struct rfct_control
127 {
128     int state;          /* state of transport endpoint */
129     int event;          /* current event */
130     int tsize;          /* max TPDU size */
131     int curcode;        /* code of current  TPDU */
132     int pending;        /* # bytes of user data waiting on line */
133     int eot_flag;       /* current incoming TPDU had EOT set */
134     int hlen;           /* TPDU header suffix remaining on line */
135     int eot;            /* current incoming DATA TPDU is last in TSDU */
136     int togo;           /* # bytes waiting to go on current outgoing DATA */
137     int qlen;           /* set by t_bind */
138     int listen;         /* we are listening */
139     int tmpfd;          /* holding space for client between listen and accept */
140     int flags;          /* set by t_open */
141     char rref[2];       /* remote reference */
142     char ltsel[TSEL_MAXLEN]; /* local transport selector */
143     int ltsel_len;
144     int oci[MAX_QLEN];   /* outstanding connect indications */
145 } *control[NOFILE];
146
147 /*
148  * In the best tradition of ThinOSI, we combine the RFC1006 and the common
149  * part of the TPDU header into one. Given the complexity of the transport
150  * layer, maybe it would have been nice to have it in a separate module
151  * for reuse - but such is hindsight.
152  * Using a bit-field like this may be dangerous. I would expect it to work
153  * fine on any 32-bit or 64-bit machine, with any decent compiler. A DOS
154  * compiler might make a mess of it, I suppose, but I wouldn't even expect
155  * that.
156  */
157 struct rfc_header
158 {
159     /* RFC1006 */
160     unsigned version:8;
161 #define RFC_VERSION 3
162     unsigned reserved:8;
163     unsigned len:16;         /* length of entire package, including header */
164     /* TPDU common fields */
165     unsigned char hlen;     /* length of TPDU-header minus length-octet */
166     unsigned char code;     /* TPDU code (and an unused 4-bit field) */
167     char suffix[100];       /* unstructured TPDU elements, for convenience */
168 };
169
170 /*
171  * The TPDU-codes used by this package.
172  */
173 #define TPDU_CODE_CREQ 0xe0
174 #define TPDU_CODE_CCON 0xd0
175 #define TPDU_CODE_DATA 0xf0
176 #define TPDU_CODE_DREQ 0x80
177
178 /*
179  * Parameters that we care about.
180  */
181 #define TPDU_PARM_TSIZE 0xc0  /* max TPDU size */
182 #define TPDU_PARM_CLGID 0xc1  /* Calling TSAP-ID */
183 #define TPDU_PARM_CLDID 0xc2  /* Called TSAP-ID */
184
185 struct tpdu_connect_header   /* length of fixed suffix: 5 octets */
186 {
187     char dst_ref[2];     /* 3-4 - not used by TP0 */
188     char src_ref[2];     /* 5-6 - not used by TP0 */
189     char class;        /* 7 - always 0 */
190 };
191
192 struct tpdu_disconnect_header /* length of fixed suffix: 5 octets */
193 {
194     char dst_ref[2];
195     char src_ref[2];
196     char reason;
197 };
198
199 struct tpdu_data_header      /* length of fixed suffix: 1 octet */
200 {
201     unsigned char nr;       /* we only use bit 7 (1 << 7), to mark EOT */
202 #define DATA_EOT (1<<7)
203 };
204
205 static int rfc_running = 0; /* Have we been initialized? */
206
207 static void init_rfc(void)
208 {
209     int i;
210
211     for (i = 0; i < NOFILE; i++)
212         control[i] = 0;
213     rfc_running = 1;
214 }
215
216 int t_open(char *name, int oflag, struct t_info *info)
217 {
218     struct rfct_control *cnt;
219     struct protoent *proto;
220     int s, i;
221
222     TRC(fprintf(stderr, "T_OPEN\n"));
223
224     if (!rfc_running)
225         init_rfc();
226
227     if (!(proto = getprotobyname("tcp")))
228         return 0;
229     if ((s = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0)
230         return 0;
231 #ifdef NONBLOCKING_OSI
232     if ((oflag & O_NONBLOCK) && fcntl(s, F_SETFL, O_NONBLOCK) < 0)
233     {
234         t_errno = TSYSERR;
235         return -1;
236     }
237 #endif
238     if (!(cnt = control[s] = malloc(sizeof(struct rfct_control))))
239     {
240         TRC(perror("malloc()"));
241         t_errno = TSYSERR;
242         return -1;
243     }
244
245     cnt->togo = 0;
246     cnt->pending = 0;
247     cnt->state = T_UNBND;
248     cnt->event = 0;
249     cnt->listen = 0;
250     cnt->qlen = 0;
251     cnt->flags = oflag;
252     cnt->tmpfd = -1;
253     cnt->ltsel_len = 0;
254     for (i = 0; i < MAX_QLEN; i++)
255         cnt->oci[i] = -1;
256
257     /*
258      * RFC1006 sets a higher than standard (TP) default max TPDU size, but the
259      * Isode seems to like to negotiate it down. We'll keep it here to be
260      * safe. Note that there's no harm in jumping it up. If it's higher
261      * than 2048, t_connect won't try to negotiate.
262      */
263     cnt->tsize = 2048;
264
265     if (info)
266     {
267         info->addr = TSEL_MAXLEN + sizeof(struct sockaddr_in) + 1;
268         info->options = 1024;
269         info->tsdu = -1; /* is this right? */
270         info->etsdu = 0;
271         info->connect = -2;
272         info->discon = -2;
273         info->servtype = T_COTS_ORD;  /* lets hope our user doesn't
274                                         try something funny. */
275     }
276     return s;
277 }
278
279 int t_connect(int fd, struct t_call *sndcall, struct t_call *rcvcall)
280 {
281     struct rfct_control *cnt = control[fd];
282     struct sockaddr_in addr;
283     char *p;
284     struct iovec vec[3]; /* RFC1006 header + T-header + parms */
285     struct rfc_header rfc;
286     struct tpdu_connect_header tpdu;
287     unsigned char pbuf[2 + TSEL_MAXLEN + 3]; /* CR parameters */
288     int plen = 0;
289
290     TRC(fprintf(stderr, "T_CONNECT\n"));
291     if (!cnt || cnt->state != T_IDLE)
292     {
293         TRC(fprintf(stderr, "TOUTSTATE\n"));
294         t_errno = TOUTSTATE;
295         return -1;
296     }
297     /* take the address apart */
298     p = sndcall->addr.buf;
299     if (*p) /* transport selector */
300     {
301         TRC(fprintf(stderr, "Tsel length is %d.\n", *p));
302         pbuf[0] = TPDU_PARM_CLDID;
303         pbuf[1] = *p;
304         memcpy(pbuf + 2, p + 1, *p);
305         plen = *p + 2;
306     }
307     p += *p + 1; /* skip tsel */
308     if (*p != sizeof(addr))
309     {
310         TRC(fprintf(stderr, "Expected  sockaddr here.\n"));
311         t_errno = TBADADDR;
312         return -1;
313     }
314     p++;
315     memcpy(&addr, p, sizeof(addr));
316     if (connect(fd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
317     {
318         t_errno = TSYSERR;
319         return -1;
320     }
321
322     /*
323      * If the default set by t_open() is higher than 2048, we don't try
324      * to negotiate, hoping that the opponent goes by the high default
325      * set by RFC1006. Otherwise, we just go for 2048 (max according to
326      * transport standard). The rest of this module doesn't really care
327      * about the size (although it's respected, of course), since we do
328      * no internal buffering.
329      */
330     if (cnt->tsize <= 2048)
331     {
332         pbuf[plen++] = TPDU_PARM_TSIZE;
333         pbuf[plen++] = 1;
334         pbuf[plen++] = 0x0b; /* request max PDU size (2048 octets) */
335     }
336
337     rfc.version = RFC_VERSION;
338     rfc.reserved = 0;
339     rfc.len =  htons(4 + 7 + plen);
340     rfc.hlen = 6 + plen;
341     rfc.code = TPDU_CODE_CREQ;
342
343     memset(tpdu.dst_ref, 0, 2);
344     memset(tpdu.src_ref, 0, 2);
345     tpdu.class = 0;
346
347     vec[0].iov_base = (caddr_t) &rfc;
348     vec[0].iov_len = 6;
349     vec[1].iov_base = (caddr_t) &tpdu;
350     vec[1].iov_len = 5;
351     vec[2].iov_base = (caddr_t) pbuf;
352     vec[2].iov_len = plen;
353
354     /*
355      * we don't expect flow-control problems on the first outgoing packet,
356      * though I suppose it'd be possible on some weird types of link.
357      */
358     if (writev(fd, vec, 3) < 4 + 7 + plen)
359     {
360         TRC(fprintf(stderr, "writev came up short. Aborting connect\n"));
361         t_errno = TSYSERR;
362         return -1;
363     }
364     cnt->state = T_OUTCON;
365     cnt->event = 0;
366     return t_rcvconnect(fd, rcvcall);
367 }
368
369 /*
370  * This needs work for asynchronous mode.
371  */
372 static int read_n(int fd, char *buf, int toget)
373 {
374     struct rfct_control *cnt = control[fd];
375     int res, got = 0;
376
377     do
378     {
379         if ((res = read(fd, buf, toget - got)) < 0)
380         {
381             if (errno == EAGAIN)
382                 t_errno = TNODATA;
383             else
384             {
385                 TRC(fprintf(stderr, "Error on read.\n"));
386                 t_errno = TSYSERR;
387             }
388             return -1;
389         }
390         if (!res) /* peer closed network connection */
391         {
392             t_errno = TLOOK;
393             cnt->event = T_DISCONNECT;  /* is this correct ? ## */
394             cnt->hlen = cnt->pending = 0;
395             cnt->state = T_IDLE; /* this can't be correct ## */
396             return 0;
397         }
398         buf += res;
399     }
400     while ((got += res) < toget);
401     return toget;
402 }
403
404 int t_rcvconnect(int fd, struct t_call *call)
405 {
406     struct rfct_control *cnt = control[fd];
407     struct tpdu_connect_header chead;
408     char buf[100], *p, *addrp;
409     struct sockaddr_in peer;
410     int len = sizeof(struct sockaddr_in);
411
412     TRC(fprintf(stderr, "T_RCVCONNECT\n"));
413     if (!cnt || cnt->state != T_OUTCON)
414     {
415         TRC(fprintf(stderr, "TOUTSTATE\n"));
416         t_errno = TOUTSTATE;
417         return -1;
418     }
419     if (!cnt->event)
420         if (t_look_wait(fd, 1) <= 0)
421             return -1;
422     if (cnt->event != T_CONNECT)
423     {
424         t_errno = TLOOK;
425         return -1;
426     }
427     /* read the rest of the CC TPDU */
428     if (read_n(fd, buf, cnt->hlen) <= 0)
429         return -1;
430     memcpy(&chead, buf, 5);
431     if (chead.class != 0)
432     {
433         TRC(fprintf(stderr, "Expected TP0, got %d\n", (int)chead.class));
434         t_errno = TSYSERR;
435         return -1;
436     }
437     if (call)
438         *(addrp = call->addr.buf) = 0;
439     cnt->hlen -= 5;
440     for (p = buf + 5; cnt->hlen > 0;)
441     {
442         switch ((unsigned char)*p)
443         {
444             case TPDU_PARM_TSIZE:
445                 cnt->tsize = 1 << *(p + 2);
446                 break;
447             case TPDU_PARM_CLDID:
448                 if (call)
449                 {
450                     if (*(p + 1) > TSEL_MAXLEN)
451                     {
452                         TRC(fprintf(stderr, "Called TSEL too long.\n"));
453                         t_errno = TSYSERR; /* Wrong.. ## */
454                         return -1;
455                     }
456                     *addrp = *(p + 1); /* length */
457                     memcpy(addrp + 1, p + 2, *(p + 1)); /* remote TSEL */
458                     addrp += *(p + 1); /* move past TSEL */
459                 }
460                 break;
461             case TPDU_PARM_CLGID: break; /* ignoring this for now */
462             default:
463                 TRC(fprintf(stderr, "Inoring CR parameter: %d\n",
464                     (unsigned char)*p));
465             /* we silently ignore anything else */
466         }
467         p++;
468         cnt->hlen -= (unsigned char) *p + 2;
469         p += (unsigned char) *p + 1;
470     }
471     addrp++; /* skip to end of addr + 1 */
472     if (call)
473     {
474         if (getpeername(fd, (struct sockaddr*) &peer, &len) < 0)
475         {
476             TRC(perror("getpeername()"));
477             t_errno = TSYSERR;
478             return -1;
479         }
480         *(addrp++) = sizeof(struct sockaddr_in);
481         memcpy(addrp, &peer, sizeof(struct sockaddr_in));
482         addrp += sizeof(struct sockaddr_in);
483         call->addr.len = addrp - call->addr.buf + 1;
484     }
485     cnt->state = T_DATAXFER;
486     cnt->event = 0;
487     return 0;
488 }
489
490 int t_snd(int fd, char *buf, unsigned nbytes, int flags)
491 {
492     struct rfct_control *cnt = control[fd];
493     struct rfc_header rfc;
494     struct iovec vec[2]; /* RFC1006 header + T-header + (user data) */
495     int writ = 0, res, towrite, eot;
496     int head_offset;
497
498     TRC(fprintf(stderr, "T_SND [%d bytes, flags %d]\n", nbytes, flags));
499     if (!cnt || cnt->state != T_DATAXFER)
500     {
501         TRC(fprintf(stderr, "Trying to write in the wrong state on fd %d.\n",
502             fd));
503         t_errno = TOUTSTATE;
504         return -1;
505     }
506     if (!nbytes)
507     {
508         t_errno = TBADDATA;
509         return -1;
510     }
511     do /* write the TSDU (segment) in chunks depending on the TPDU max size */
512     {
513         if (cnt->togo > 0) /* we have a partial TPDU on the wire */
514         {
515             TRC(fprintf(stderr, "  writing continuation block (%d)\n",
516                 cnt->togo));
517             if ((res = write(fd, buf, cnt->togo)) < 0)
518             {
519                 if (errno == EAGAIN)
520                 {
521                     t_errno = TFLOW;
522                     return -1;
523                 }
524                 cnt->togo -= res;
525                 return res;
526             }
527             writ += res;
528             cnt->togo = 0;
529             TRC(fprintf(stderr, "  wrote %d, total %d\n", res, writ));
530         }
531         else /* prepare and send (possibly partial) header */
532         {
533             towrite = nbytes - writ;
534             if (towrite + 3 + 4 > cnt->tsize)
535                 towrite = cnt->tsize - (3 + 4); /* space for DATA header */
536             rfc.version = RFC_VERSION;
537             rfc.reserved = 0;
538             rfc.len = htons(towrite + 4 + 3); /* RFC1006 length */
539             rfc.hlen = 2;
540             rfc.code = TPDU_CODE_DATA;
541             if (flags & T_MORE || towrite + writ < nbytes)
542                 eot = 0;
543             else
544                 eot = 1;
545             rfc.suffix[0] = eot << 7; /* DATA EOT marker */
546             if (cnt->togo < 0)
547                 head_offset = 7 + cnt->togo;
548             else
549                 head_offset = 0;
550             vec[0].iov_base = (caddr_t) (char*)&rfc + head_offset;
551             vec[0].iov_len = 7 - head_offset;
552             vec[1].iov_base = (caddr_t) buf + writ;
553             vec[1].iov_len = towrite;
554             TRC(fprintf(stderr, "  sending beg of block (%d+%d)\n",
555                 7 - head_offset, towrite));
556             if ((res = writev(fd, vec, 2)) < 0)
557             {
558                 TRC(fprintf(stderr, "  write returned -1\n"));
559                 /* thwarted by flow control */
560                 if (errno == EAGAIN)
561                 {
562                     if (writ)
563                         return writ;
564                     else
565                     {
566                         t_errno = TFLOW;
567                         return -1;
568                     }
569                 }
570                 else
571                     t_errno = TSYSERR;
572                 return -1;
573             }
574             /* somewhat thwarted */
575             else if (res < towrite + 7 - head_offset)
576             {
577                 /*
578                  * Write came up short. We assume that this is a flow-
579                  * control thing, and return immediately. Maybe it'd
580                  * be better to take another loop, and generate an
581                  * actual EAGAIN from write?
582                  */
583                 TRC(fprintf(stderr, "  write returned %d\n", res));
584                 if (res < 7 - head_offset) /* we didn't send a full header */
585                 {
586                     cnt->togo = -(7 - head_offset - res);
587                     t_errno = TFLOW;
588                     return -1;
589                 }
590                 else if ((res -= 7 - head_offset) < towrite) /* not all data */
591                 {
592                     cnt->togo = towrite - res;
593                     return nbytes - (writ + res);
594                 }
595             }
596             else /* whew... nonblocking I/O is hard work */
597             {
598                 cnt->togo = 0;
599                 writ += res - (7 - head_offset);
600             }
601         }
602     }
603     while (writ < nbytes);
604     TRC(fprintf(stderr, "  finishing with %d written\n", nbytes));
605     return nbytes;
606 }
607
608 int _t_rcv(int fd, char *buf, unsigned nbytes, int *flags)
609 {
610     struct rfct_control *cnt = control[fd];
611     struct tpdu_data_header dhead;
612     int res, got = 0;
613     struct iovec vec[2];
614     int toget;
615
616     TRC(fprintf(stderr, "T_RCV [nbytes=%d, flags=0x%x]\n", nbytes, *flags));
617     if (!cnt || cnt->state != T_DATAXFER)
618     {
619         t_errno = TOUTSTATE;
620         return -1;
621     }
622     *flags = 0;
623     do /* loop until we have a full TSDU or an error */
624     {
625         if (!cnt->event)
626             if (t_look_wait(fd, 0) <= 0)
627                 return -1;
628         if (cnt->event != T_DATA)
629         {
630             t_errno = TLOOK;
631             return -1;
632         }
633         if (cnt->hlen)  /* beginning of block */
634         {
635             TRC(fprintf(stderr, "  Beginning of TPDU\n"));
636             if (cnt->hlen > 2)
637             {
638                 TRC(fprintf(stderr, "We can't handle parameters to DATA\n"));
639                 t_errno = TSYSERR;
640                 return -1;
641             }
642             toget = cnt->pending;
643             if (toget > nbytes - got)
644                 toget = nbytes - got;
645             TRC(fprintf(stderr, "  toget=%d\n", toget));
646             vec[0].iov_base = (caddr_t) &dhead;
647             vec[0].iov_len = 1;
648             vec[1].iov_base = (caddr_t) buf + got;
649             vec[1].iov_len = toget;
650             if ((res = readv(fd, vec, 2)) < 0)
651             {
652                 TRC(perror("readv()"));
653                 if (errno == EAGAIN)
654                 {
655                     if (got)  /* we got some data in previous cycle */
656                         break;
657                     t_errno = TNODATA; /* no data */
658                     return -1;
659                 }
660                 t_errno = TSYSERR;
661                 return -1;
662             }
663             TRC(fprintf(stderr, "  readv() returned %d\n", res));
664             if (res == 0)
665             {
666                 t_errno = TLOOK;
667                 cnt->event = T_DISCONNECT;
668                 return -1;
669             }
670             got += res - 1;
671             cnt->eot_flag = (dhead.nr & DATA_EOT) >> 7;
672             cnt->hlen = 0;
673             cnt->pending -= got;
674             TRC(fprintf(stderr, "  Got total of %d octets, %d pending\n",
675                 got, cnt->pending));
676         }
677         else /* continuation */
678         {
679             TRC(fprintf(stderr, "  Reading middle of TPDU\n"));
680             toget = cnt->pending;
681             if (toget > nbytes - got)
682                 toget = nbytes - got;
683             TRC(fprintf(stderr, "  toget=%d\n", toget));
684             if ((res = read(fd, buf + got, toget)) < 0)
685             {
686                 TRC(perror("read()"));
687                 if (errno == EAGAIN)
688                 {
689                     if (got)  /* we got some data in previous cycle */
690                         break;
691                     t_errno = TNODATA; /* no data */
692                     return -1;
693                 }
694                 t_errno = TSYSERR;
695                 return -1;
696             }
697             TRC(fprintf(stderr, "  read() returned %d\n", res));
698             if (res == 0)
699             {
700                 t_errno = TLOOK;
701                 cnt->event = T_DISCONNECT;
702                 return -1;
703             }
704             got += res;
705             cnt->pending -= res;
706             TRC(fprintf(stderr, "  Got total of %d octets, %d pending\n",
707                 got, cnt->pending));
708         }
709         TRC(fprintf(stderr, "  bottom of loop: pending=%d, got=%d\n",
710             cnt->pending, got));
711     }
712     while (cnt->pending && got < nbytes);
713     *flags = cnt->pending || !cnt->eot_flag ? T_MORE : 0;
714     TRC(fprintf(stderr, "  flags=0x%x\n", *flags));
715     if (!cnt->pending)
716         cnt->event = 0;
717     TRC(fprintf(stderr, "  Return value: %d\n", got));
718     memset(buf + got, 0, 10);
719     return got;
720 }
721
722 int t_rcv(int fd, char *buf, unsigned nbytes, int *flags)
723 {
724     int res;
725     int total = 0;
726
727     do
728     {
729         if ((res = _t_rcv(fd, buf, nbytes, flags)) <= 0)
730             return res;
731         buf += res;
732         nbytes -= res;
733         total += res;
734     }
735     while (*flags & T_MORE && nbytes > 0);
736     return total;
737 }
738
739 int t_close(int fd)
740 {
741     struct rfct_control *cnt = control[fd];
742
743     TRC(fprintf(stderr, "T_CLOSE\n"));
744     if (!cnt || cnt->state == T_UNINIT)
745     {
746         TRC(fprintf(stderr, "Trying to close a bad fd.\n"));
747         t_errno = TBADF;
748         return -1;
749     }
750     free(cnt);
751     return close(fd);
752 }
753
754 /*
755  * This isn't right, obviously.
756  */
757 int t_error(char *errmsg)
758 {
759     TRC(fprintf(stderr, "T_ERROR\n"));
760     fprintf(stderr, "t_error(t_errno=%d):", t_errno);
761     perror(errmsg);
762     return 0;
763 }    
764
765 /*
766  * Put a select in here!!
767  */
768 static int t_look_wait(int fd, int wait)
769 {
770     struct rfct_control *cnt = control[fd];
771     struct rfc_header head;
772     int res;
773
774     TRC(fprintf(stderr, "T_LOOK\n"));
775     if (!cnt || cnt->state == T_UNINIT)
776     {
777         t_errno = TBADF;
778         return -1;
779     }
780     if (cnt->event)
781         return cnt->event;
782     if (cnt->state == T_IDLE && cnt->tmpfd < 0)
783         return T_LISTEN; /* the only possible type of event */
784     if ((res = read_n(fd, (char*) &head, 6)) < 0)
785         return -1;
786     if (res == 0)
787     {
788         TRC(fprintf(stderr, "Network disconnect\n"));
789         return cnt->event = T_DISCONNECT;
790     }
791     TRC(fprintf(stderr, "t_look got %d bytes\n", res));
792     if (head.version != RFC_VERSION)
793     {
794         TRC(fprintf(stderr, "Got bad RFC1006 version in t_look: %d.\n",
795             head.version));
796         t_errno = TSYSERR; /* should signal protocol error, somehow ## */
797         return -1;
798     }
799     cnt->curcode = head.code;
800     cnt->hlen = head.hlen - 1; /* length of header suffix */
801     cnt->pending = ntohs(head.len) - 6 - cnt->hlen;
802     TRC(fprintf(stderr, "t_look: len=%d, code=0x%2.2x, hlen=%d.\n",
803         cnt->pending + 6, cnt->curcode, cnt->hlen));
804     switch (cnt->curcode)
805     {
806         case TPDU_CODE_CREQ: cnt->event = T_LISTEN; break;
807         case TPDU_CODE_CCON: cnt->event = T_CONNECT; break;
808         case TPDU_CODE_DATA: cnt->event = T_DATA; break;
809         case TPDU_CODE_DREQ: cnt->event = T_DISCONNECT; break; 
810         default:
811             TRC(fprintf(stderr, "t_look: Bad package: 0x%2.2x.\n", cnt->curcode));
812             t_errno = TSYSERR;  /* protocol error */
813             return -1;
814     }
815     return cnt->event;
816 }    
817
818 int t_look(int fd)
819 {
820     return t_look_wait(fd, 0);
821 }
822
823 /*
824  * If the user doesn't provide a NSAP, and qlen > 0, we'll do a default
825  * server bind. If qlen==0, we won't bind at all - connect will do that
826  * for us.
827  */
828 int t_bind(int fd, struct t_bind *req, struct t_bind *ret)
829 {
830     struct rfct_control *cnt = control[fd];
831     struct sockaddr_in addr;
832     char *p;
833     int got_addr = 0;
834
835     TRC(fprintf(stderr, "T_BIND\n"));
836     if (!cnt || cnt->state != T_UNBND)
837     {
838         TRC(fprintf(stderr, "Bad state\n"));
839         t_errno = TOUTSTATE;
840         return -1;
841     }
842     cnt->ltsel_len = 0;
843     if (req)
844     {
845         cnt->qlen = req->qlen < MAX_QLEN ? req->qlen : MAX_QLEN;
846         if (req->addr.len)
847         {
848             p = req->addr.buf;
849             if (*p > TSEL_MAXLEN)
850             {
851                 TRC(fprintf(stderr, "Tsel too large.\n"));
852                 t_errno = TBADADDR;
853                 return -1;
854             }
855             cnt->ltsel_len = *p;
856             if (cnt->ltsel_len)
857                 memcpy(cnt->ltsel, p + 1, cnt->ltsel_len);
858             p += cnt->ltsel_len + 1;
859             if (*p < sizeof(addr))
860             {
861                 TRC(fprintf(stderr, "W: No NSAP provided for local bind\n"));
862             }
863             else
864             {
865                 memcpy(&addr, p + 1, sizeof(addr));
866                 got_addr = 1;
867             }
868         }
869     }
870     else
871         cnt->qlen = 0;
872     if (!got_addr) /* user didn't give an address - local bind */
873     {
874         addr.sin_family = AF_INET;
875         addr.sin_addr.s_addr = INADDR_ANY;
876         if (cnt->qlen)
877             addr.sin_port = htons(RFC_DEFAULT_PORT);
878         else /* we'll leave binding to connect - just set dummy */
879             addr.sin_port = 0; /* dummy for ret */
880     }
881     if (cnt->qlen && bind(fd, (struct sockaddr *) &addr,
882          sizeof(addr)) < 0 )
883     {
884         TRC(perror("bind()"));
885         t_errno = TSYSERR; /* this should be refined */
886         return -1;
887     }
888     if (cnt->qlen)
889     {
890         if (listen(fd, cnt->qlen) < 0)
891         {
892             t_errno = TSYSERR;
893             return -1;
894         }
895         cnt->listen = 1;
896         TRC(fprintf(stderr, "  listen OK\n"));
897     }
898     cnt->state = T_IDLE;
899     /* All right! Now let's give something back, if our user wants it */
900     if (ret)
901     {
902         ret->qlen = cnt->qlen;
903         if (ret->addr.maxlen < (ret->addr.len = cnt->ltsel_len + 2 +
904             sizeof(addr)))
905         {
906             /* No space - but we're still bound */
907             t_errno = TBUFOVFLW;
908             ret->addr.len = 0;
909             return -1;
910         }
911         p = ret->addr.buf;
912         *(p++) = cnt->ltsel_len;
913         if (cnt->ltsel_len)
914             memcpy(p, cnt->ltsel, cnt->ltsel_len);
915         p += cnt->ltsel_len;
916         *(p++) = sizeof(addr);
917         memcpy(p, &addr, sizeof(addr));
918     }
919     return 0;
920 }    
921
922 /*
923  * need to consult RFC1006 on these. I think they just map to close()...
924  */
925 int t_snddis(int fd, struct t_call *call)
926 {
927     TRC(fprintf(stderr, "T_SNDDIS\n"));
928     return 0;
929 }
930
931 int t_rcvdis(int fd, struct t_discon *discon)
932 {
933     struct rfct_control *cnt = control[fd];
934     struct tpdu_disconnect_header chead;
935     char udata[64], buf[256], *p;
936
937     TRC(fprintf(stderr, "T_RCVDIS\n"));
938     if (!cnt) 
939     {
940         TRC(fprintf(stderr, "TOUTSTATE\n"));
941         t_errno = TOUTSTATE;
942         return -1;
943     }
944     if (!cnt->event)
945         if (t_look_wait(fd, 1) <= 0)
946             return -1;
947     if (cnt->event != T_DISCONNECT)
948     {
949         t_errno = TLOOK;
950         return -1;
951     }
952     /* read the rest of the DR TPDU */
953     if (read_n(fd, buf, cnt->hlen) <= 0)
954         return -1;
955     memcpy(&chead, buf, 5);
956     cnt->hlen -= 5;
957     for (p = buf + 5; cnt->hlen > 0;)
958     {
959         switch ((unsigned char)*p)
960         {
961             default:
962                 TRC(fprintf(stderr, "Inoring RD parameter: %d\n",
963                     (unsigned char)*p));
964             /* we silently ignore anything else */
965         }
966         p++;
967         cnt->hlen -= (unsigned char) *p + 2;
968         p += (unsigned char) *p + 1;
969     }
970     if (cnt->pending)
971     {
972         if (read_n(fd, udata, cnt->pending) < cnt->pending)
973         {
974             TRC(fprintf(stderr, "Unable to read user data\n"));
975             t_errno = TSYSERR;
976             return -1;
977         }
978     }
979     cnt->state = T_IDLE;  /* should we close transport? */
980     cnt->event = 0;
981     if (discon)
982     {
983         discon->sequence = -1; /* TOFIX */
984         discon->reason = chead.reason;
985         TRC(fprintf(stderr, "Diconnect reason %d\n", chead.reason));
986         if (cnt->pending > discon->udata.maxlen)
987         {
988             t_errno = TBUFOVFLW;
989             return -1;
990         }
991         if (cnt->pending)
992         {
993             memcpy(discon->udata.buf, udata, cnt->pending);
994             udata[cnt->pending] = '\0';
995             TRC(fprintf(stderr, "Discon udata: '%s'\n", udata));
996         }
997         discon->udata.len = cnt->pending;
998     }
999     return 0;
1000 }    
1001
1002 /*
1003  * fix memory management, you bad Sebastian!
1004  */
1005 int t_free(char *ptr, int struct_type)
1006 {
1007     TRC(fprintf(stderr, "T_FREE\n"));
1008     free(ptr);
1009     return 0;
1010 }    
1011
1012 char *t_alloc(int fd, int struct_type, int fields)
1013 {
1014     char *r = malloc(1024);
1015     if (!r)
1016         return 0;
1017     TRC(fprintf(stderr, "T_ALLOC\n"));
1018     memset(r, 0, 1024);
1019     return r;
1020 }
1021
1022 /*
1023  * this is required for t_listen... if a system doesn't support dup2(), we're
1024  * in trouble: We might not be able to do nonblocking listen. Time will tell.
1025  */
1026 static int switch_fds(int fd1, int fd2)
1027 {
1028     int tmp;
1029     struct rfct_control *tmpc;
1030     
1031     TRC(fprintf(stderr, "Switching fds %d <--> %d\n", fd1, fd2));
1032     if ((tmp = dup(fd1)) < 0 ||
1033         dup2(fd2, fd1) < 0 ||
1034         dup2(tmp, fd2) < 0 ||
1035         close(tmp) < 0)
1036             return -1;
1037     tmpc = control[fd1];
1038     control[fd1] = control[fd2];
1039     control[fd2] = tmpc;
1040     return 0;
1041 }
1042
1043 static int rcvconreq(int fd, struct t_call *call)
1044 {
1045     struct rfct_control *cnt = control[fd];
1046     struct rfct_control *new = control[cnt->tmpfd];
1047     struct tpdu_connect_header chead;
1048     char buf[100];
1049     char *p, *addrp = 0;
1050     struct sockaddr_in addr;
1051     int len = sizeof(struct sockaddr_in);
1052     int qslot;
1053
1054     TRC(fprintf(stderr, "RCVCONRES\n"));
1055     if (!call)
1056     {
1057         t_errno = TSYSERR;
1058         return -1;
1059     }
1060     for (qslot = 0; qslot < cnt->qlen; qslot++)
1061         if (cnt->oci[qslot] < 0)
1062             break;
1063     if (qslot == cnt->qlen) /* no free slots - shouldn't happen here */
1064     {
1065         t_errno = TBADF;
1066         return -1;
1067     }
1068     /* read the rest of the CREQ TPDU */
1069     if (read_n(cnt->tmpfd, buf, new->hlen) <= 0)
1070         return -1;
1071     memcpy(&chead, buf, 5);
1072     if (chead.class != 0)
1073     {
1074         TRC(fprintf(stderr, "Expected TP0, got %d\n", (int)chead.class));
1075         t_errno = TSYSERR;
1076         return -1;
1077     }
1078     memcpy(new->rref, chead.src_ref, 2); /* we'll echo this back at her */
1079     new->hlen -= 5;
1080     if (call && call->addr.maxlen)
1081         *(addrp = call->addr.buf) = 0;
1082     for (p = buf + 5; new->hlen > 0;)
1083     {
1084         switch ((unsigned char)*p)
1085         {
1086             case TPDU_PARM_TSIZE:
1087                 new->tsize = 1 << *(p + 2); /* we go with their max */
1088                 break;
1089             case TPDU_PARM_CLDID: break; /* ignore */
1090             case TPDU_PARM_CLGID:
1091                 if (addrp)
1092                 {
1093                     if (*(p + 1) > TSEL_MAXLEN)
1094                     {
1095                         TRC(fprintf(stderr, "Called TSEL too long.\n"));
1096                         t_errno = TSYSERR; /* Wrong.. ## */
1097                         return -1;
1098                     }
1099                     *addrp = *(p + 1); /* length */
1100                     memcpy(addrp + 1, p + 2, *(p + 1)); /* remote TSEL */
1101                     addrp += *(p + 1); /* move past TSEL */
1102                 }
1103                 break;
1104             /* we silently ignore preferred TPDU size and others */
1105         }
1106         p++;
1107         new->hlen -= (unsigned char) *p + 2;
1108         p += (unsigned char) *p + 1;
1109     }
1110     if (addrp)
1111     {
1112         addrp++;
1113         if (getpeername(cnt->tmpfd, (struct sockaddr*) &addr, &len) < 0)
1114         {
1115             TRC(perror("getpeername()"));
1116             t_errno = TSYSERR;
1117             return -1;
1118         }
1119         *(addrp++) = sizeof(struct sockaddr_in);
1120         memcpy(addrp, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
1121     }
1122     new->event = 0;
1123     cnt->event = 0;
1124     cnt->oci[qslot] = cnt->tmpfd; /* move the new semi-connection to oci */
1125     call->sequence = qslot;
1126     cnt->tmpfd = -1;
1127     cnt->state = T_INCON;
1128     return 0;
1129 }
1130
1131 /*
1132  * This construction is tricky in async mode: listen calls accept, which
1133  * generates a new fd for the association. Call is supposed to return
1134  * immediately if there is no CR on the line (which is highly likely),
1135  * and t-user will then try to use select() on the wrong socket, waiting for
1136  * the transport level package. 
1137  *
1138  * Compared to the upper-level u_* routines, we have one major handicap
1139  * and one major benefit: We *have* to handle two different endpoints
1140  * after t_listen (which includes sockets accept); and we have access
1141  * to dup2().
1142  *
1143  * If the endpoint is configured for nonblocking I/O, and listen is not
1144  * able to immediately acquire the CR, the two fds  are switched, so that
1145  * subsequent selects take place on the data socket, rather than the
1146  * listener socket.
1147  *
1148  * At any rate, the data socket is saved, so that it can later be given
1149  * to the user in t_accept().
1150  */
1151 int t_listen(int fd, struct t_call *call)
1152 {
1153     struct rfct_control *cnt = control[fd], *new;
1154     struct sockaddr_in addr;
1155     int addrlen = sizeof(struct sockaddr_in);
1156     int tmpfd_is_new = 0; /* we've just accept()ed a connection */
1157     int event, i;
1158
1159     TRC(fprintf(stderr, "T_LISTEN\n"));
1160     if (!cnt || cnt->state != T_IDLE)
1161     {
1162         TRC(fprintf(stderr, "T_listen expects state==T_IDLE (wrong?)\n"));
1163         t_errno = TOUTSTATE;
1164         return -1;
1165     }
1166     if (!cnt->qlen)
1167     {
1168         t_errno = TBADQLEN;
1169         return -1;
1170     }
1171     for (i = 0; i < cnt->qlen; i++)
1172         if (cnt->oci[i] < 0)
1173             break;
1174     if (i == cnt->qlen) /* no slots in queue */
1175     {
1176         TRC(fprintf(stderr, "No more space in queue\n"));
1177         t_errno = TBADF; /* what would be more correct? */
1178         return -1;
1179     }
1180     if (cnt->tmpfd < 0)
1181     {
1182         TRC(fprintf(stderr, "Accept..."));
1183         if ((cnt->tmpfd = accept(fd, (struct sockaddr*) &addr, &addrlen)) < 0)
1184         {
1185             if (errno == EWOULDBLOCK)
1186             {
1187                 t_errno = TNODATA;
1188                 TRC(fprintf(stderr, "Accept returned WOULDBLOCK\n"));
1189             }
1190             else
1191             {
1192                 TRC(fprintf(stderr, "accept failed\n"));
1193                 t_errno = TSYSERR;
1194             }
1195             return -1;
1196         }
1197 #ifdef NONBLOCKING_OSI
1198         if ((cnt->flags & O_NONBLOCK) && fcntl(cnt->tmpfd, F_SETFL,
1199             O_NONBLOCK) < 0)
1200         {
1201             t_errno = TSYSERR;
1202             return -1;
1203         }
1204 #endif
1205         tmpfd_is_new = 1;
1206         TRC(fprintf(stderr, "Accept OK\n"));
1207         if (!(new = control[cnt->tmpfd] = malloc(sizeof(*new))))
1208         {
1209             TRC(perror("malloc()"));
1210             t_errno = TSYSERR;
1211             return -1;
1212         }
1213         new->togo = 0;
1214         new->pending = 0;
1215         new->state = T_IDLE;
1216         new->event = 0;
1217         new->listen = 0;
1218         new->qlen = cnt->qlen;
1219         new->flags = cnt->flags;
1220         new->tmpfd = cnt->tmpfd;
1221         new->ltsel_len = 0;
1222         for (i = 0; i < MAX_QLEN; i++)
1223             new->oci[i] = -1;
1224     }
1225     /* we got a network connection. Now try to read transport CREQ TPDU */
1226     if ((event = t_look_wait(tmpfd_is_new ? cnt->tmpfd : fd, 1)) < 0)
1227     {
1228         if (t_errno == TNODATA)
1229         {
1230             if (tmpfd_is_new)
1231             {
1232                 /*
1233                  * We give the user something to select on for the incoming
1234                  * CR. Switch the new association with the listener socket.
1235                  */
1236                 TRC(fprintf(stderr, "Switching FDs\n"));
1237                 if (switch_fds(cnt->tmpfd, fd) < 0)
1238                 {
1239                     t_errno = TSYSERR;
1240                     return -1;
1241                 }
1242             }
1243             return -1;
1244         }
1245         else
1246         {
1247             t_close(cnt->tmpfd);
1248             cnt->tmpfd = -1;
1249         }
1250         return -1; /* t_look & t_read hopefully set up the right errcodes */
1251     }
1252     else
1253     {
1254         /* We got something! */
1255         if (event != T_LISTEN)
1256         {
1257             TRC(fprintf(stderr, "Expected T_LISTEN\n"));
1258             t_errno = TLOOK;
1259             return -1;
1260         }
1261         /*
1262          * switch back the fds, if necessary */
1263         if (!tmpfd_is_new && switch_fds(fd, cnt->tmpfd) < 0)
1264         {
1265             t_errno = TSYSERR;
1266             return -1;
1267         }
1268         if (rcvconreq(fd, call) < 0)
1269         {
1270             t_close(cnt->tmpfd);
1271             cnt->tmpfd = -1;
1272         }
1273         return 0;
1274     }
1275 }    
1276
1277 /*
1278  * There's no clean mapping of this onto the socket interface. If someone
1279  * wants it, we could fake it by first closing the socket, and then
1280  * opening a new one and doing a dup2. That should be functionally
1281  * equivalent(?).
1282  */
1283 int t_unbind(int fd)
1284 {
1285     fprintf(stderr, "T_UNBIND [not supported by transport implementation]\n");
1286     t_errno = TNOTSUPPORT;
1287     return -1;
1288 }    
1289
1290 int t_accept(int fd, int resfd, struct t_call *call)
1291 {
1292     struct rfct_control *listener = control[fd]; /* listener handle */
1293     struct rfct_control *new; /* new, semi-complete association */
1294     struct rfct_control *res; /* resulting association handle */
1295     struct iovec vec[3]; /* RFC1006 header + T-header + parm */
1296     struct rfc_header rfc;
1297     struct tpdu_connect_header tpdu;
1298     unsigned char parm[3 + TSEL_MAXLEN + 2];
1299     int i, newfd;
1300     
1301     TRC(fprintf(stderr, "T_ACCEPT\n"));
1302     if (!listener || listener->state != T_INCON)
1303     {
1304         TRC(fprintf(stderr, "TOUTSTATE\n"));
1305         t_errno = TOUTSTATE;
1306         return -1;
1307     }
1308     /* Get the semi-connection */
1309     if (call->sequence >= listener->qlen || listener->oci[call->sequence] < 0)
1310     {
1311         TRC(fprintf(stderr, "TBADSEQ\n"));
1312         t_errno = TBADSEQ;
1313         return -1;
1314     }
1315     new = control[(newfd = listener->oci[call->sequence])];
1316     listener->oci[call->sequence] = -1;
1317     res = control[resfd];
1318     if (!res)
1319     {
1320         t_errno = TBADF;
1321         return -1;
1322     }
1323     if (res != listener) /* move the new connection */
1324     {
1325         TRC(fprintf(stderr, "  Moving to new fd (%d)\n", resfd));
1326         if (res->state != T_IDLE || res->qlen)
1327         {
1328             TRC(fprintf(stderr, "Trying to move new assc. to bad fd.\n"));
1329             t_errno = TBADF;
1330             return -1;
1331         }
1332         dup2(newfd, resfd); /* closes resfd */
1333         close(newfd);
1334         control[resfd] = new;
1335         /* transfer local bindings from res */
1336         if (res->ltsel_len)
1337             memcpy(control[resfd]->ltsel, res->ltsel, res->ltsel_len);
1338         control[resfd]->ltsel_len = res->ltsel_len;
1339         free(res);
1340         res = control[resfd];
1341         listener->event = 0;
1342         listener->state = T_IDLE;
1343     }
1344     else /* lose our listener */
1345     {
1346         TRC(fprintf(stderr, "  Moving to listener fd\n"));
1347         for (i = 0; i < listener->qlen; i++)
1348             if (listener->oci[i] >= 0)
1349             {
1350                 TRC(fprintf(stderr, "Still conn indications on listener\n"));
1351                 t_errno = TBADF;
1352                 return -1;
1353             }
1354         dup2(newfd, fd);
1355         close(newfd);
1356         control[fd] = new;
1357         if (listener->ltsel_len)
1358             memcpy(control[resfd]->ltsel, listener->ltsel, listener->ltsel_len);
1359         control[resfd]->ltsel_len = listener->ltsel_len;
1360         free(listener);
1361         res = control[resfd];
1362     }
1363     rfc.version = RFC_VERSION;
1364     rfc.reserved = 0;
1365     rfc.code = TPDU_CODE_CCON;
1366
1367     memcpy(tpdu.src_ref, "AA", 2);
1368     memcpy(tpdu.dst_ref, res->rref, 2); /* echo back at 'em */
1369     tpdu.class = 0;
1370
1371     /* grant them their TPDU size */
1372     parm[0] = TPDU_PARM_TSIZE;
1373     parm[1] = 1;
1374     for (i = 7; i <= 11 && (1 << i) < res->tsize; i++) ; /* encode TPDU size */
1375     parm[2] = i;
1376     /* give our TSEL. ## Must we echo theirs, if given? check spec */
1377     /* I thought it was ok to give an empty TSEL. Does it have semantic sig? */
1378     parm[3] = TPDU_PARM_CLDID;
1379     parm[4] = res->ltsel_len; 
1380     if (res->ltsel_len)
1381         memcpy(parm + 5, res->ltsel, res->ltsel_len);
1382
1383     rfc.len =  htons(4 + 7 + 3 + 2 + res->ltsel_len);
1384     rfc.hlen = 6 + 3 + 2 + res->ltsel_len;
1385     vec[0].iov_base = (caddr_t) &rfc;
1386     vec[0].iov_len = 6;
1387     vec[1].iov_base = (caddr_t) &tpdu;
1388     vec[1].iov_len = 5;
1389     vec[2].iov_base = (caddr_t) parm;
1390     vec[2].iov_len = 3 + 2 + res->ltsel_len;
1391     if (writev(resfd, vec, 3) < 4 + 7 + 3 + (2 + res->ltsel_len))
1392     {
1393         TRC(fprintf(stderr, "writev came up short. Aborting connect\n"));
1394         t_errno = TSYSERR;
1395         return -1;
1396     }
1397     res->state = T_DATAXFER;
1398     res->event = 0;
1399     return 0;
1400 }    
1401
1402 int t_getstate(int fd)
1403 {
1404     TRC(fprintf(stderr, "T_GETSTATE\n"));
1405     return control[fd] ? control[fd]->state : T_UNINIT;
1406 }