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