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