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