95d1d04c9ed74baac61ef22e3a7a1e67ca239db3
[sip-router] / tcp_read.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2001-2003 Fhg Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 /*
28  * History:
29  * --------
30  * 2002-12-??  created by andrei.
31  * 2003-02-10  zero term before calling receive_msg & undo afterwards (andrei)
32  * 2003-05-13  l: (short form of Content-Length) is now recognized (andrei)
33  * 2003-07-01  tcp_read & friends take no a single tcp_connection 
34  *              parameter & they set c->state to S_CONN_EOF on eof (andrei)
35  * 2003-07-04  fixed tcp EOF handling (possible infinite loop) (andrei)
36  */
37
38 #ifdef USE_TCP
39
40 #include <stdio.h>
41 #include <errno.h>
42 #include <string.h>
43
44
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <sys/select.h>
48 #include <sys/socket.h>
49
50 #include <unistd.h>
51 #include <stdlib.h> /* for abort() */
52
53
54 #include "dprint.h"
55 #include "tcp_conn.h"
56 #include "pass_fd.h"
57 #include "globals.h"
58 #include "receive.h"
59 #include "timer.h"
60 #include "ut.h"
61 #ifdef USE_TLS
62 #include "tls/tls_server.h"
63 #endif
64
65
66
67 /* reads next available bytes
68  * return number of bytes read, 0 on EOF or -1 on error,
69  * on EOF it also sets c->state to S_CONN_EOF
70  * (to distinguish from reads that would block which could return 0)
71  * sets also r->error */
72 int tcp_read(struct tcp_connection *c)
73 {
74         int bytes_free, bytes_read;
75         struct tcp_req *r;
76         int fd;
77
78         r=&c->req;
79         fd=c->fd;
80         bytes_free=TCP_BUF_SIZE- (int)(r->pos - r->buf);
81         
82         if (bytes_free==0){
83                 LOG(L_ERR, "ERROR: tcp_read: buffer overrun, dropping\n");
84                 r->error=TCP_REQ_OVERRUN;
85                 return -1;
86         }
87 again:
88         bytes_read=read(fd, r->pos, bytes_free);
89
90         if(bytes_read==-1){
91                 if (errno == EWOULDBLOCK || errno == EAGAIN){
92                         return 0; /* nothing has been read */
93                 }else if (errno == EINTR) goto again;
94                 else{
95                         LOG(L_ERR, "ERROR: tcp_read: error reading: %s\n",strerror(errno));
96                         r->error=TCP_READ_ERROR;
97                         return -1;
98                 }
99         }else if (bytes_read==0){
100                 c->state=S_CONN_EOF;
101                 DBG("tcp_read: EOF on %p, FD %d\n", c, fd);
102         }
103 #ifdef EXTRA_DEBUG
104         DBG("tcp_read: read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos);
105 #endif
106         r->pos+=bytes_read;
107         return bytes_read;
108 }
109
110
111
112 /* reads all headers (until double crlf), & parses the content-length header
113  * (WARNING: ineficient, tries to reuse receive_msg but will go through
114  * the headers twice [once here looking for Content-Length and for the end
115  * of the headers and once in receive_msg]; a more speed eficient version will
116  * result in either major code duplication or major changes to the receive code)
117  * returns number of bytes read & sets r->state & r->body
118  * when either r->body!=0 or r->state==H_BODY =>
119  * all headers have been read. It should be called in a while loop.
120  * returns < 0 if error or 0 if EOF */
121 int tcp_read_headers(struct tcp_connection *c)
122 {
123         int bytes, remaining;
124         char *p;
125         struct tcp_req* r;
126         
127         #define crlf_default_skip_case \
128                                         case '\n': \
129                                                 r->state=H_LF; \
130                                                 break; \
131                                         default: \
132                                                 r->state=H_SKIP
133         
134         #define content_len_beg_case \
135                                         case ' ': \
136                                         case '\t': \
137                                                 if (!r->has_content_len) r->state=H_STARTWS; \
138                                                 else r->state=H_SKIP; \
139                                                         /* not interested if we already found one */ \
140                                                 break; \
141                                         case 'C': \
142                                         case 'c': \
143                                                 if(!r->has_content_len) r->state=H_CONT_LEN1; \
144                                                 else r->state=H_SKIP; \
145                                                 break; \
146                                         case 'l': \
147                                         case 'L': \
148                                                 /* short form for Content-Length */ \
149                                                 if (!r->has_content_len) r->state=H_L_COLON; \
150                                                 else r->state=H_SKIP; \
151                                                 break
152                                                 
153         #define change_state(upper, lower, newstate)\
154                                         switch(*p){ \
155                                                 case upper: \
156                                                 case lower: \
157                                                         r->state=(newstate); break; \
158                                                 crlf_default_skip_case; \
159                                         }
160         
161         #define change_state_case(state0, upper, lower, newstate)\
162                                         case state0: \
163                                                           change_state(upper, lower, newstate); \
164                                                           p++; \
165                                                           break
166
167
168         r=&c->req;
169         /* if we still have some unparsed part, parse it first, don't do the read*/
170         if (r->parsed<r->pos){
171                 bytes=0;
172         }else{
173 #ifdef USE_TLS
174                 if (c->type==PROTO_TLS)
175                         bytes=tls_read(c);
176                 else
177 #endif
178                         bytes=tcp_read(c);
179                 if (bytes<=0) return bytes;
180         }
181         p=r->parsed;
182         
183         while(p<r->pos && r->error==TCP_REQ_OK){
184                 switch((unsigned char)r->state){
185                         case H_BODY: /* read the body*/
186                                 remaining=r->pos-p;
187                                 if (remaining>r->bytes_to_go) remaining=r->bytes_to_go;
188                                 r->bytes_to_go-=remaining;
189                                 p+=remaining;
190                                 if (r->bytes_to_go==0){
191                                         r->complete=1;
192                                         goto skip;
193                                 }
194                                 break;
195                                 
196                         case H_SKIP:
197                                 /* find lf, we are in this state if we are not interested
198                                  * in anything till end of line*/
199                                 p=q_memchr(p, '\n', r->pos-p);
200                                 if (p){
201                                         p++;
202                                         r->state=H_LF;
203                                 }else{
204                                         p=r->pos;
205                                 }
206                                 break;
207                                 
208                         case H_LF:
209                                 /* terminate on LF CR LF or LF LF */
210                                 switch (*p){
211                                         case '\r':
212                                                 r->state=H_LFCR;
213                                                 break;
214                                         case '\n':
215                                                 /* found LF LF */
216                                                 r->state=H_BODY;
217                                                 if (r->has_content_len){
218                                                         r->body=p+1;
219                                                         r->bytes_to_go=r->content_len;
220                                                         if (r->bytes_to_go==0){
221                                                                 r->complete=1;
222                                                                 p++;
223                                                                 goto skip;
224                                                         }
225                                                 }else{
226                                                         DBG("tcp_read_headers: ERROR: no clen, p=%X\n",
227                                                                         *p);
228                                                         r->error=TCP_REQ_BAD_LEN;
229                                                 }
230                                                 break;
231                                         content_len_beg_case;
232                                         default: 
233                                                 r->state=H_SKIP;
234                                 }
235                                 p++;
236                                 break;
237                         case H_LFCR:
238                                 if (*p=='\n'){
239                                         /* found LF CR LF */
240                                         r->state=H_BODY;
241                                         if (r->has_content_len){
242                                                 r->body=p+1;
243                                                 r->bytes_to_go=r->content_len;
244                                                 if (r->bytes_to_go==0){
245                                                         r->complete=1;
246                                                         p++;
247                                                         goto skip;
248                                                 }
249                                         }else{
250                                                 DBG("tcp_read_headers: ERROR: no clen, p=%X\n",
251                                                                         *p);
252                                                 r->error=TCP_REQ_BAD_LEN;
253                                         }
254                                 }else r->state=H_SKIP;
255                                 p++;
256                                 break;
257                                 
258                         case H_STARTWS:
259                                 switch (*p){
260                                         content_len_beg_case;
261                                         crlf_default_skip_case;
262                                 }
263                                 p++;
264                                 break;
265                         case H_SKIP_EMPTY:
266                                 switch (*p){
267                                         case '\n':
268                                         case '\r':
269                                         case ' ':
270                                         case '\t':
271                                                 /* skip empty lines */
272                                                 break;
273                                         case 'C': 
274                                         case 'c': 
275                                                 r->state=H_CONT_LEN1; 
276                                                 r->start=p;
277                                                 break;
278                                         case 'l':
279                                         case 'L':
280                                                 /* short form for Content-Length */
281                                                 r->state=H_L_COLON;
282                                                 r->start=p;
283                                                 break;
284                                         default:
285                                                 r->state=H_SKIP;
286                                                 r->start=p;
287                                 };
288                                 p++;
289                                 break;
290                         change_state_case(H_CONT_LEN1,  'O', 'o', H_CONT_LEN2);
291                         change_state_case(H_CONT_LEN2,  'N', 'n', H_CONT_LEN3);
292                         change_state_case(H_CONT_LEN3,  'T', 't', H_CONT_LEN4);
293                         change_state_case(H_CONT_LEN4,  'E', 'e', H_CONT_LEN5);
294                         change_state_case(H_CONT_LEN5,  'N', 'n', H_CONT_LEN6);
295                         change_state_case(H_CONT_LEN6,  'T', 't', H_CONT_LEN7);
296                         change_state_case(H_CONT_LEN7,  '-', '_', H_CONT_LEN8);
297                         change_state_case(H_CONT_LEN8,  'L', 'l', H_CONT_LEN9);
298                         change_state_case(H_CONT_LEN9,  'E', 'e', H_CONT_LEN10);
299                         change_state_case(H_CONT_LEN10, 'N', 'n', H_CONT_LEN11);
300                         change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12);
301                         change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13);
302                         change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON);
303                         
304                         case H_L_COLON:
305                                 switch(*p){
306                                         case ' ':
307                                         case '\t':
308                                                 break; /* skip space */
309                                         case ':':
310                                                 r->state=H_CONT_LEN_BODY;
311                                                 break;
312                                         crlf_default_skip_case;
313                                 };
314                                 p++;
315                                 break;
316                         
317                         case  H_CONT_LEN_BODY:
318                                 switch(*p){
319                                         case ' ':
320                                         case '\t':
321                                                 break; /* eat space */
322                                         case '0':
323                                         case '1':
324                                         case '2':
325                                         case '3':
326                                         case '4':
327                                         case '5':
328                                         case '6':
329                                         case '7':
330                                         case '8':
331                                         case '9':
332                                                 r->state=H_CONT_LEN_BODY_PARSE;
333                                                 r->content_len=(*p-'0');
334                                                 break;
335                                         /*FIXME: content lenght on different lines ! */
336                                         crlf_default_skip_case;
337                                 }
338                                 p++;
339                                 break;
340                                 
341                         case H_CONT_LEN_BODY_PARSE:
342                                 switch(*p){
343                                         case '0':
344                                         case '1':
345                                         case '2':
346                                         case '3':
347                                         case '4':
348                                         case '5':
349                                         case '6':
350                                         case '7':
351                                         case '8':
352                                         case '9':
353                                                 r->content_len=r->content_len*10+(*p-'0');
354                                                 break;
355                                         case '\r':
356                                         case ' ':
357                                         case '\t': /* FIXME: check if line contains only WS */
358                                                 r->state=H_SKIP;
359                                                 r->has_content_len=1;
360                                                 break;
361                                         case '\n':
362                                                 /* end of line, parse succesfull */
363                                                 r->state=H_LF;
364                                                 r->has_content_len=1;
365                                                 break;
366                                         default:
367                                                 LOG(L_ERR, "ERROR: tcp_read_headers: bad "
368                                                                 "Content-Length header value, unexpected "
369                                                                 "char %c in state %d\n", *p, r->state);
370                                                 r->state=H_SKIP; /* try to find another?*/
371                                 }
372                                 p++;
373                                 break;
374                         
375                         default:
376                                 LOG(L_CRIT, "BUG: tcp_read_headers: unexpected state %d\n",
377                                                 r->state);
378                                 abort();
379                 }
380         }
381 skip:
382         r->parsed=p;
383         return bytes;
384 }
385
386
387
388 int tcp_read_req(struct tcp_connection* con)
389 {
390         int bytes;
391         int resp;
392         long size;
393         struct tcp_req* req;
394         int s;
395         char c;
396                 
397                 resp=CONN_RELEASE;
398                 s=con->fd;
399                 req=&con->req;
400 #ifdef USE_TLS
401                 if (con->type==PROTO_TLS){
402                         if (tls_fix_read_conn(con)!=0){
403                                 resp=CONN_ERROR;
404                                 goto end_req;
405                         }
406                         if(con->state!=S_CONN_OK) goto end_req; /* not enough data */
407                 }
408 #endif
409
410 again:
411                 if(req->error==TCP_REQ_OK){
412                         bytes=tcp_read_headers(con);
413 #ifdef EXTRA_DEBUG
414                                                 /* if timeout state=0; goto end__req; */
415                         DBG("read= %d bytes, parsed=%d, state=%d, error=%d\n",
416                                         bytes, (int)(req->parsed-req->start), req->state,
417                                         req->error );
418                         DBG("tcp_read_req: last char=%X, parsed msg=\n%.*s\n",
419                                         *(req->parsed-1), (int)(req->parsed-req->start),
420                                         req->start);
421 #endif
422                         if (bytes==-1){
423                                 LOG(L_ERR, "ERROR: tcp_read_req: error reading \n");
424                                 resp=CONN_ERROR;
425                                 goto end_req;
426                         }
427                         /* eof check:
428                          * is EOF if eof on fd and req.  not complete yet,
429                          * if req. is complete we might have a second unparsed
430                          * request after it, so postpone release_with_eof
431                          */
432                         if ((con->state==S_CONN_EOF) && (req->complete==0)) {
433                                 DBG( "tcp_read_req: EOF\n");
434                                 resp=CONN_EOF;
435                                 goto end_req;
436                         }
437                 
438                 }
439                 if (req->error!=TCP_REQ_OK){
440                         LOG(L_ERR,"ERROR: tcp_read_req: bad request, state=%d, error=%d "
441                                           "buf:\n%.*s\nparsed:\n%.*s\n", req->state, req->error,
442                                           (int)(req->pos-req->buf), req->buf,
443                                           (int)(req->parsed-req->start), req->start);
444                         DBG("- received from: port %d\n", con->rcv.src_port);
445                         print_ip("- received from: ip ",&con->rcv.src_ip, "\n");
446                         resp=CONN_ERROR;
447                         goto end_req;
448                 }
449                 if (req->complete){
450 #ifdef EXTRA_DEBUG
451                         DBG("tcp_read_req: end of header part\n");
452                         DBG("- received from: port %d\n", con->rcv.src_port);
453                         print_ip("- received from: ip ", &con->rcv.src_ip, "\n");
454                         DBG("tcp_read_req: headers:\n%.*s.\n",
455                                         (int)(req->body-req->start), req->start);
456 #endif
457                         if (req->has_content_len){
458                                 DBG("tcp_read_req: content-length= %d\n", req->content_len);
459 #ifdef EXTRA_DEBUG
460                                 DBG("tcp_read_req: body:\n%.*s\n", req->content_len,req->body);
461 #endif
462                         }else{
463                                 req->error=TCP_REQ_BAD_LEN;
464                                 LOG(L_ERR, "ERROR: tcp_read_req: content length not present or"
465                                                 " unparsable\n");
466                                 resp=CONN_ERROR;
467                                 goto end_req;
468                         }
469                         /* if we are here everything is nice and ok*/
470                         resp=CONN_RELEASE;
471 #ifdef EXTRA_DEBUG
472                         DBG("calling receive_msg(%p, %d, )\n",
473                                         req->start, (int)(req->parsed-req->start));
474 #endif
475                         /* rcv.bind_address should always be !=0 */
476                         bind_address=con->rcv.bind_address;
477                         /* just for debugging use sendipv4 as receiving socket  FIXME*/
478                         /*
479                         if (con->rcv.dst_ip.af==AF_INET6){
480                                 bind_address=sendipv6_tcp;
481                         }else{
482                                 bind_address=sendipv4_tcp;
483                         }
484                         */
485                         con->rcv.proto_reserved1=con->id; /* copy the id */
486                         c=*req->parsed; /* ugly hack: zero term the msg & save the
487                                                            previous char, req->parsed should be ok
488                                                            because we always alloc BUF_SIZE+1 */
489                         *req->parsed=0;
490                         if (receive_msg(req->start, req->parsed-req->start, &con->rcv)<0){
491                                 *req->parsed=c;
492                                 resp=CONN_ERROR;
493                                 goto end_req;
494                         }
495                         *req->parsed=c;
496                         
497                         /* prepare for next request */
498                         size=req->pos-req->parsed;
499                         if (size) memmove(req->buf, req->parsed, size);
500 #ifdef EXTRA_DEBUG
501                         DBG("tcp_read_req: preparing for new request, kept %ld bytes\n",
502                                         size);
503 #endif
504                         req->pos=req->buf+size;
505                         req->parsed=req->buf;
506                         req->start=req->buf;
507                         req->body=0;
508                         req->error=TCP_REQ_OK;
509                         req->state=H_SKIP_EMPTY;
510                         req->complete=req->content_len=req->has_content_len=0;
511                         req->bytes_to_go=0;
512                         /* if we still have some unparsed bytes, try to  parse them too*/
513                         if (size) goto again;
514                         else if (con->state==S_CONN_EOF){
515                                 DBG( "tcp_read_req: EOF after reading complete request\n");
516                                 resp=CONN_EOF;
517                         }
518                         
519                 }
520                 
521                 
522         end_req:
523                 
524                 return resp;
525 }
526
527
528
529 void release_tcpconn(struct tcp_connection* c, long state, int unix_sock)
530 {
531         long response[2];
532         
533                 DBG( "releasing con %p, state %ld, fd=%d, id=%d\n",
534                                 c, state, c->fd, c->id);
535                 DBG(" extra_data %p\n", c->extra_data);
536                 /* release req & signal the parent */
537                 if (c->fd!=-1) close(c->fd);
538                 /* errno==EINTR, EWOULDBLOCK a.s.o todo */
539                 response[0]=(long)c;
540                 response[1]=state;
541                 if (send_all(unix_sock, response, sizeof(response))<=0)
542                         LOG(L_ERR, "ERROR: release_tcpconn: send_all failed\n");
543 }
544
545
546
547 void tcp_receive_loop(int unix_sock)
548 {
549         struct tcp_connection* list; /* list with connections in use */
550         struct tcp_connection* con;
551         struct tcp_connection* c_next;
552         int n;
553         int nfds;
554         int s;
555         long resp;
556         fd_set master_set;
557         fd_set sel_set;
558         int maxfd;
559         struct timeval timeout;
560         int ticks;
561         
562         
563         /* init */
564         list=con=0;
565         FD_ZERO(&master_set);
566         FD_SET(unix_sock, &master_set);
567         maxfd=unix_sock;
568         
569         /* listen on the unix socket for the fd */
570         for(;;){
571                         timeout.tv_sec=TCP_CHILD_SELECT_TIMEOUT;
572                         timeout.tv_usec=0;
573                         sel_set=master_set;
574                         nfds=select(maxfd+1, &sel_set, 0 , 0 , &timeout);
575 #ifdef EXTRA_DEBUG
576                         for (n=0; n<maxfd; n++){
577                                 if (FD_ISSET(n, &sel_set)) 
578                                         DBG("tcp receive: FD %d is set\n", n);
579                         }
580 #endif
581                         if (nfds<0){
582                                 if (errno==EINTR) continue; /* just a signal */
583                                 /* errors */
584                                 LOG(L_ERR, "ERROR: tcp_receive_loop: select:(%d) %s\n", errno,
585                                         strerror(errno));
586                                 continue;
587                         }
588                         if (FD_ISSET(unix_sock, &sel_set)){
589                                 nfds--;
590                                 /* a new conn from "main" */
591                                 n=receive_fd(unix_sock, &con, sizeof(con), &s);
592                                 if (n<0){
593                                         if (errno == EWOULDBLOCK || errno == EAGAIN ||
594                                                         errno == EINTR){
595                                                 goto skip;
596                                         }else{
597                                                 LOG(L_CRIT,"BUG: tcp_receive_loop: read_fd: %s\n",
598                                                         strerror(errno));
599                                                 abort(); /* big error*/
600                                         }
601                                 }
602                                 DBG("received n=%d con=%p, fd=%d\n", n, con, s);
603                                 if (n==0){
604                                         LOG(L_ERR, "WARNING: tcp_receive_loop: 0 bytes read\n");
605                                         goto skip;
606                                 }
607                                 if (con==0){
608                                         LOG(L_CRIT, "BUG: tcp_receive_loop: null pointer\n");
609                                         goto skip;
610                                 }
611                                 con->fd=s;
612                                 if (s==-1) {
613                                         LOG(L_ERR, "ERROR: tcp_receive_loop: read_fd:"
614                                                                         "no fd read\n");
615                                         resp=CONN_ERROR;
616                                         con->state=S_CONN_BAD;
617                                         release_tcpconn(con, resp, unix_sock);
618                                         goto skip;
619                                 }
620                                 con->timeout=get_ticks()+TCP_CHILD_TIMEOUT;
621                                 FD_SET(s, &master_set);
622                                 if (maxfd<s) maxfd=s;
623                                 if (con==list){
624                                         LOG(L_CRIT, "BUG: tcp_receive_loop: duplicate"
625                                                         " connection recevied: %p, id %d, fd %d, refcnt %d"
626                                                         " state %d (n=%d)\n", con, con->id, con->fd,
627                                                         con->refcnt, con->state, n);
628                                         resp=CONN_ERROR;
629                                         release_tcpconn(con, resp, unix_sock);
630                                         goto skip; /* try to recover */
631                                 }
632                                 tcpconn_listadd(list, con, c_next, c_prev);
633                         }
634 skip:
635                         ticks=get_ticks();
636                         for (con=list; con ; con=c_next){
637                                 c_next=con->c_next; /* safe for removing*/
638 #ifdef EXTRA_DEBUG
639                                 DBG("tcp receive: list fd=%d, id=%d, timeout=%d, refcnt=%d\n",
640                                                 con->fd, con->id, con->timeout, con->refcnt);
641 #endif
642                                 if (con->state<0){
643                                         /* S_CONN_BAD or S_CONN_ERROR, remove it */
644                                         resp=CONN_ERROR;
645                                         FD_CLR(con->fd, &master_set);
646                                         tcpconn_listrm(list, con, c_next, c_prev);
647                                         con->state=S_CONN_BAD;
648                                         release_tcpconn(con, resp, unix_sock);
649                                         continue;
650                                 }
651                                 if (nfds && FD_ISSET(con->fd, &sel_set)){
652 #ifdef EXTRA_DEBUG
653                                         DBG("tcp receive: match, fd:isset\n");
654 #endif
655                                         nfds--;
656                                         resp=tcp_read_req(con);
657                                         
658                                         if (resp<0){
659                                                 FD_CLR(con->fd, &master_set);
660                                                 tcpconn_listrm(list, con, c_next, c_prev);
661                                                 con->state=S_CONN_BAD;
662                                                 release_tcpconn(con, resp, unix_sock);
663                                         }else{
664                                                 /* update timeout */
665                                                 con->timeout=ticks+TCP_CHILD_TIMEOUT;
666                                         }
667                                 }else{
668                                         /* timeout */
669                                         if (con->timeout<=ticks){
670                                                 /* expired, return to "tcp main" */
671                                                 DBG("tcp_receive_loop: %p expired (%d, %d)\n",
672                                                                 con, con->timeout, ticks);
673                                                 resp=CONN_RELEASE;
674                                                 FD_CLR(con->fd, &master_set);
675                                                 tcpconn_listrm(list, con, c_next, c_prev);
676                                                 release_tcpconn(con, resp, unix_sock);
677                                         }
678                                 }
679                         }
680                 
681         }
682 }
683
684
685 #if 0
686 int main(int argv, char** argc )
687 {
688         printf("starting tests\n");
689         tcp_receive_loop();
690 }
691
692 #endif
693
694 #endif