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