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