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