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