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