tcp_read.c
5b532c7f
 /*
  * $Id$
  *
  * Copyright (C) 2001-2003 Fhg Fokus
  *
  * This file is part of ser, a free SIP server.
  *
  * ser is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
8fc80c33
 /*
  * History:
  * --------
  * 2002-12-??  created by andrei.
  * 2003-02-10  zero term before calling receive_msg & undo afterwards (andrei)
607fcb90
  * 2003-05-13  l: (short form of Content-Length) is now recognized (andrei)
28427aa4
  * 2003-07-01  tcp_read & friends take no a single tcp_connection 
  *              parameter & they set c->state to S_CONN_EOF on eof (andrei)
6ee07a24
  * 2003-07-04  fixed tcp EOF handling (possible infinite loop) (andrei)
8fc80c33
  */
5b532c7f
 
 #ifdef USE_TCP
 
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
 
 
 #include <sys/time.h>
 #include <sys/types.h>
b988daef
 #include <sys/select.h>
5b532c7f
 #include <sys/socket.h>
 
 #include <unistd.h>
6c6659cb
 #include <stdlib.h> /* for abort() */
5b532c7f
 
 
6a157851
 #include "dprint.h"
5b532c7f
 #include "tcp_conn.h"
 #include "pass_fd.h"
6a157851
 #include "globals.h"
 #include "receive.h"
6ee62314
 #include "timer.h"
ab130758
 #include "ut.h"
28427aa4
 #ifdef USE_TLS
 #include "tls/tls_server.h"
 #endif
5b532c7f
 
 
 
 /* reads next available bytes
  * return number of bytes read, 0 on EOF or -1 on error,
28427aa4
  * on EOF it also sets c->state to S_CONN_EOF
  * (to distinguish from reads that would block which could return 0)
5b532c7f
  * sets also r->error */
28427aa4
 int tcp_read(struct tcp_connection *c)
5b532c7f
 {
 	int bytes_free, bytes_read;
28427aa4
 	struct tcp_req *r;
 	int fd;
 
 	r=&c->req;
 	fd=c->fd;
5b532c7f
 	bytes_free=TCP_BUF_SIZE- (int)(r->pos - r->buf);
 	
 	if (bytes_free==0){
6ee62314
 		LOG(L_ERR, "ERROR: tcp_read: buffer overrun, dropping\n");
5b532c7f
 		r->error=TCP_REQ_OVERRUN;
 		return -1;
 	}
 again:
6bc40dea
 	bytes_read=read(fd, r->pos, bytes_free);
5b532c7f
 
 	if(bytes_read==-1){
 		if (errno == EWOULDBLOCK || errno == EAGAIN){
 			return 0; /* nothing has been read */
 		}else if (errno == EINTR) goto again;
 		else{
6ee62314
 			LOG(L_ERR, "ERROR: tcp_read: error reading: %s\n",strerror(errno));
5b532c7f
 			r->error=TCP_READ_ERROR;
 			return -1;
 		}
28427aa4
 	}else if (bytes_read==0){
6ee07a24
 		c->state=S_CONN_EOF;
 		DBG("tcp_read: EOF on %p, FD %d\n", c, fd);
5b532c7f
 	}
f2e456c3
 #ifdef EXTRA_DEBUG
ab130758
 	DBG("tcp_read: read %d bytes:\n%.*s\n", bytes_read, bytes_read, r->pos);
f2e456c3
 #endif
5b532c7f
 	r->pos+=bytes_read;
 	return bytes_read;
 }
 
 
 
6a157851
 /* reads all headers (until double crlf), & parses the content-length header
f2e456c3
  * (WARNING: ineficient, tries to reuse receive_msg but will go through
  * the headers twice [once here looking for Content-Length and for the end
  * of the headers and once in receive_msg]; a more speed eficient version will
  * result in either major code duplication or major changes to the receive code)
5b532c7f
  * returns number of bytes read & sets r->state & r->body
  * when either r->body!=0 or r->state==H_BODY =>
  * all headers have been read. It should be called in a while loop.
  * returns < 0 if error or 0 if EOF */
28427aa4
 int tcp_read_headers(struct tcp_connection *c)
5b532c7f
 {
6a157851
 	int bytes, remaining;
5b532c7f
 	char *p;
28427aa4
 	struct tcp_req* r;
5b532c7f
 	
6a157851
 	#define crlf_default_skip_case \
 					case '\n': \
 						r->state=H_LF; \
 						break; \
 					default: \
 						r->state=H_SKIP
 	
 	#define content_len_beg_case \
 					case ' ': \
 					case '\t': \
 						if (!r->has_content_len) r->state=H_STARTWS; \
 						else r->state=H_SKIP; \
 							/* not interested if we already found one */ \
 						break; \
 					case 'C': \
 					case 'c': \
 						if(!r->has_content_len) r->state=H_CONT_LEN1; \
 						else r->state=H_SKIP; \
607fcb90
 						break; \
 					case 'l': \
 					case 'L': \
 						/* short form for Content-Length */ \
 						if (!r->has_content_len) r->state=H_L_COLON; \
 						else r->state=H_SKIP; \
 						break
6a157851
 						
 	#define change_state(upper, lower, newstate)\
 					switch(*p){ \
 						case upper: \
 						case lower: \
 							r->state=(newstate); break; \
 						crlf_default_skip_case; \
 					}
 	
 	#define change_state_case(state0, upper, lower, newstate)\
 					case state0: \
 							  change_state(upper, lower, newstate); \
 							  p++; \
 							  break
 
 
28427aa4
 	r=&c->req;
ab130758
 	/* if we still have some unparsed part, parse it first, don't do the read*/
 	if (r->parsed<r->pos){
 		bytes=0;
 	}else{
28427aa4
 #ifdef USE_TLS
 		if (c->type==PROTO_TLS)
 			bytes=tls_read(c);
 		else
 #endif
 			bytes=tcp_read(c);
ab130758
 		if (bytes<=0) return bytes;
 	}
5b532c7f
 	p=r->parsed;
 	
6a157851
 	while(p<r->pos && r->error==TCP_REQ_OK){
c0c6207a
 		switch((unsigned char)r->state){
6a157851
 			case H_BODY: /* read the body*/
 				remaining=r->pos-p;
 				if (remaining>r->bytes_to_go) remaining=r->bytes_to_go;
 				r->bytes_to_go-=remaining;
 				p+=remaining;
 				if (r->bytes_to_go==0){
 					r->complete=1;
 					goto skip;
 				}
 				break;
 				
 			case H_SKIP:
 				/* find lf, we are in this state if we are not interested
 				 * in anything till end of line*/
ab130758
 				p=q_memchr(p, '\n', r->pos-p);
5b532c7f
 				if (p){
 					p++;
 					r->state=H_LF;
 				}else{
 					p=r->pos;
 				}
 				break;
 				
 			case H_LF:
 				/* terminate on LF CR LF or LF LF */
6a157851
 				switch (*p){
 					case '\r':
 						r->state=H_LFCR;
 						break;
 					case '\n':
 						/* found LF LF */
 						r->state=H_BODY;
 						if (r->has_content_len){
 							r->body=p+1;
 							r->bytes_to_go=r->content_len;
 							if (r->bytes_to_go==0){
 								r->complete=1;
ab130758
 								p++;
6a157851
 								goto skip;
 							}
 						}else{
ab130758
 							DBG("tcp_read_headers: ERROR: no clen, p=%X\n",
 									*p);
6a157851
 							r->error=TCP_REQ_BAD_LEN;
 						}
 						break;
 					content_len_beg_case;
 					default: 
 						r->state=H_SKIP;
 				}
5b532c7f
 				p++;
 				break;
 			case H_LFCR:
 				if (*p=='\n'){
 					/* found LF CR LF */
 					r->state=H_BODY;
6a157851
 					if (r->has_content_len){
 						r->body=p+1;
 						r->bytes_to_go=r->content_len;
 						if (r->bytes_to_go==0){
 							r->complete=1;
ab130758
 							p++;
6a157851
 							goto skip;
 						}
 					}else{
ab130758
 						DBG("tcp_read_headers: ERROR: no clen, p=%X\n",
 									*p);
6a157851
 						r->error=TCP_REQ_BAD_LEN;
 					}
 				}else r->state=H_SKIP;
5b532c7f
 				p++;
 				break;
 				
6a157851
 			case H_STARTWS:
 				switch (*p){
 					content_len_beg_case;
 					crlf_default_skip_case;
 				}
 				p++;
 				break;
c0c6207a
 			case H_SKIP_EMPTY:
 				switch (*p){
 					case '\n':
 					case '\r':
 					case ' ':
 					case '\t':
 						/* skip empty lines */
 						break;
 					case 'C': 
 					case 'c': 
 						r->state=H_CONT_LEN1; 
 						r->start=p;
 						break;
607fcb90
 					case 'l':
 					case 'L':
 						/* short form for Content-Length */
 						r->state=H_L_COLON;
 						r->start=p;
 						break;
c0c6207a
 					default:
 						r->state=H_SKIP;
 						r->start=p;
 				};
 				p++;
 				break;
6a157851
 			change_state_case(H_CONT_LEN1,  'O', 'o', H_CONT_LEN2);
 			change_state_case(H_CONT_LEN2,  'N', 'n', H_CONT_LEN3);
 			change_state_case(H_CONT_LEN3,  'T', 't', H_CONT_LEN4);
 			change_state_case(H_CONT_LEN4,  'E', 'e', H_CONT_LEN5);
 			change_state_case(H_CONT_LEN5,  'N', 'n', H_CONT_LEN6);
 			change_state_case(H_CONT_LEN6,  'T', 't', H_CONT_LEN7);
 			change_state_case(H_CONT_LEN7,  '-', '_', H_CONT_LEN8);
 			change_state_case(H_CONT_LEN8,  'L', 'l', H_CONT_LEN9);
 			change_state_case(H_CONT_LEN9,  'E', 'e', H_CONT_LEN10);
 			change_state_case(H_CONT_LEN10, 'N', 'n', H_CONT_LEN11);
 			change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12);
 			change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13);
 			change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON);
 			
 			case H_L_COLON:
 				switch(*p){
 					case ' ':
 					case '\t':
 						break; /* skip space */
 					case ':':
 						r->state=H_CONT_LEN_BODY;
 						break;
 					crlf_default_skip_case;
 				};
 				p++;
 				break;
 			
 			case  H_CONT_LEN_BODY:
 				switch(*p){
 					case ' ':
 					case '\t':
 						break; /* eat space */
 					case '0':
 					case '1':
 					case '2':
 					case '3':
 					case '4':
 					case '5':
 					case '6':
 					case '7':
 					case '8':
 					case '9':
 						r->state=H_CONT_LEN_BODY_PARSE;
 						r->content_len=(*p-'0');
 						break;
 					/*FIXME: content lenght on different lines ! */
 					crlf_default_skip_case;
 				}
 				p++;
 				break;
 				
 			case H_CONT_LEN_BODY_PARSE:
 				switch(*p){
 					case '0':
 					case '1':
 					case '2':
 					case '3':
 					case '4':
 					case '5':
 					case '6':
 					case '7':
 					case '8':
c0c6207a
 					case '9':
6a157851
 						r->content_len=r->content_len*10+(*p-'0');
 						break;
 					case '\r':
 					case ' ':
 					case '\t': /* FIXME: check if line contains only WS */
 						r->state=H_SKIP;
 						r->has_content_len=1;
 						break;
 					case '\n':
 						/* end of line, parse succesfull */
 						r->state=H_LF;
 						r->has_content_len=1;
 						break;
 					default:
 						LOG(L_ERR, "ERROR: tcp_read_headers: bad "
 								"Content-Length header value, unexpected "
 								"char %c in state %d\n", *p, r->state);
 						r->state=H_SKIP; /* try to find another?*/
 				}
 				p++;
 				break;
 			
5b532c7f
 			default:
6ee62314
 				LOG(L_CRIT, "BUG: tcp_read_headers: unexpected state %d\n",
 						r->state);
5b532c7f
 				abort();
 		}
 	}
6a157851
 skip:
5b532c7f
 	r->parsed=p;
 	return bytes;
 }
 
 
 
6ee62314
 int tcp_read_req(struct tcp_connection* con)
 {
 	int bytes;
0c5da34b
 	int resp;
6ee62314
 	long size;
 	struct tcp_req* req;
 	int s;
8fc80c33
 	char c;
6ee62314
 		
0c5da34b
 		resp=CONN_RELEASE;
6ee62314
 		s=con->fd;
 		req=&con->req;
28427aa4
 #ifdef USE_TLS
 		if (con->type==PROTO_TLS){
 			if (con->state==S_CONN_ACCEPT){
8e807134
 				if (tls_accept(con, 0)!=0){
28427aa4
 					resp=CONN_ERROR;
 					goto end_req;
 				}
 				if(con->state!=S_CONN_OK) goto end_req; /* not enough data */
 			}
 			if(con->state==S_CONN_CONNECT){
8e807134
 				if (tls_connect(con, 0)!=0){
28427aa4
 					resp=CONN_ERROR;
 					goto end_req;
 				}
 				if(con->state!=S_CONN_OK) goto end_req; /* not enough data */
 			}
 		}
 #endif
 
ab130758
 again:
6ee07a24
 		if(req->error==TCP_REQ_OK){
28427aa4
 			bytes=tcp_read_headers(con);
f2e456c3
 #ifdef EXTRA_DEBUG
6ee62314
 						/* if timeout state=0; goto end__req; */
 			DBG("read= %d bytes, parsed=%d, state=%d, error=%d\n",
b988daef
 					bytes, (int)(req->parsed-req->start), req->state,
 					req->error );
ab130758
 			DBG("tcp_read_req: last char=%X, parsed msg=\n%.*s\n",
b988daef
 					*(req->parsed-1), (int)(req->parsed-req->start),
 					req->start);
f2e456c3
 #endif
6ee62314
 			if (bytes==-1){
 				LOG(L_ERR, "ERROR: tcp_read_req: error reading \n");
0c5da34b
 				resp=CONN_ERROR;
6ee62314
 				goto end_req;
 			}
6ee07a24
 			/* eof check:
 			 * is EOF if eof on fd and req.  not complete yet,
 			 * if req. is complete we might have a second unparsed
 			 * request after it, so postpone release_with_eof
 			 */
 			if ((con->state==S_CONN_EOF) && (req->complete==0)) {
6ee62314
 				DBG( "tcp_read_req: EOF\n");
0c5da34b
 				resp=CONN_EOF;
6ee62314
 				goto end_req;
 			}
 		
 		}
 		if (req->error!=TCP_REQ_OK){
ab130758
 			LOG(L_ERR,"ERROR: tcp_read_req: bad request, state=%d, error=%d "
 					  "buf:\n%.*s\nparsed:\n%.*s\n", req->state, req->error,
b988daef
 					  (int)(req->pos-req->buf), req->buf,
 					  (int)(req->parsed-req->start), req->start);
9c01c860
 			DBG("- received from: port %d\n", con->rcv.src_port);
 			print_ip("- received from: ip ",&con->rcv.src_ip, "\n");
0c5da34b
 			resp=CONN_ERROR;
6ee62314
 			goto end_req;
 		}
 		if (req->complete){
f2e456c3
 #ifdef EXTRA_DEBUG
6ee62314
 			DBG("tcp_read_req: end of header part\n");
9c01c860
 			DBG("- received from: port %d\n", con->rcv.src_port);
 			print_ip("- received from: ip ", &con->rcv.src_ip, "\n");
6ee62314
 			DBG("tcp_read_req: headers:\n%.*s.\n",
b988daef
 					(int)(req->body-req->start), req->start);
f2e456c3
 #endif
6ee62314
 			if (req->has_content_len){
 				DBG("tcp_read_req: content-length= %d\n", req->content_len);
f2e456c3
 #ifdef EXTRA_DEBUG
6ee62314
 				DBG("tcp_read_req: body:\n%.*s\n", req->content_len,req->body);
f2e456c3
 #endif
6ee62314
 			}else{
 				req->error=TCP_REQ_BAD_LEN;
 				LOG(L_ERR, "ERROR: tcp_read_req: content length not present or"
 						" unparsable\n");
0c5da34b
 				resp=CONN_ERROR;
6ee62314
 				goto end_req;
 			}
 			/* if we are here everything is nice and ok*/
0c5da34b
 			resp=CONN_RELEASE;
f2e456c3
 #ifdef EXTRA_DEBUG
6ee62314
 			DBG("calling receive_msg(%p, %d, )\n",
ab130758
 					req->start, (int)(req->parsed-req->start));
f2e456c3
 #endif
 			/* rcv.bind_address should always be !=0 */
21e0d5c1
 			bind_address=con->rcv.bind_address;
f2e456c3
 			/* just for debugging use sendipv4 as receiving socket  FIXME*/
21e0d5c1
 			/*
7ec958f3
 			if (con->rcv.dst_ip.af==AF_INET6){
 				bind_address=sendipv6_tcp;
 			}else{
 				bind_address=sendipv4_tcp;
 			}
21e0d5c1
 			*/
f2f969dd
 			con->rcv.proto_reserved1=con->id; /* copy the id */
8fc80c33
 			c=*req->parsed; /* ugly hack: zero term the msg & save the
 							   previous char, req->parsed should be ok
 							   because we always alloc BUF_SIZE+1 */
 			*req->parsed=0;
c0c6207a
 			if (receive_msg(req->start, req->parsed-req->start, &con->rcv)<0){
8fc80c33
 				*req->parsed=c;
0c5da34b
 				resp=CONN_ERROR;
 				goto end_req;
 			}
8fc80c33
 			*req->parsed=c;
6ee62314
 			
 			/* prepare for next request */
c0c6207a
 			size=req->pos-req->parsed;
 			if (size) memmove(req->buf, req->parsed, size);
f2e456c3
 #ifdef EXTRA_DEBUG
6ee62314
 			DBG("tcp_read_req: preparing for new request, kept %ld bytes\n",
 					size);
f2e456c3
 #endif
6ee62314
 			req->pos=req->buf+size;
 			req->parsed=req->buf;
c0c6207a
 			req->start=req->buf;
6ee62314
 			req->body=0;
 			req->error=TCP_REQ_OK;
c0c6207a
 			req->state=H_SKIP_EMPTY;
6ee62314
 			req->complete=req->content_len=req->has_content_len=0;
 			req->bytes_to_go=0;
ab130758
 			/* if we still have some unparsed bytes, try to  parse them too*/
 			if (size) goto again;
6ee07a24
 			else if (con->state==S_CONN_EOF){
 				DBG( "tcp_read_req: EOF after reading complete request\n");
 				resp=CONN_EOF;
 			}
6ee62314
 			
 		}
 		
 		
 	end_req:
744a2341
 		
0c5da34b
 		return resp;
6ee62314
 }
 
 
 
 void release_tcpconn(struct tcp_connection* c, long state, int unix_sock)
 {
 	long response[2];
 	
30e8735f
 		DBG( "releasing con %p, state %ld, fd=%d, id=%d\n",
 				c, state, c->fd, c->id);
744a2341
 		DBG(" extra_data %p\n", c->extra_data);
6ee62314
 		/* release req & signal the parent */
 		if (c->fd!=-1) close(c->fd);
 		/* errno==EINTR, EWOULDBLOCK a.s.o todo */
 		response[0]=(long)c;
 		response[1]=state;
 		write(unix_sock, response, sizeof(response));
 }
 
 
5b532c7f
 
 void tcp_receive_loop(int unix_sock)
 {
6bc40dea
 	struct tcp_connection* list; /* list with connections in use */
 	struct tcp_connection* con;
6ee62314
 	struct tcp_connection* c_next;
5b532c7f
 	int n;
6bc40dea
 	int nfds;
5b532c7f
 	int s;
0c5da34b
 	long resp;
6bc40dea
 	fd_set master_set;
 	fd_set sel_set;
 	int maxfd;
 	struct timeval timeout;
6ee62314
 	int ticks;
5b532c7f
 	
 	
6bc40dea
 	/* init */
 	list=con=0;
 	FD_ZERO(&master_set);
 	FD_SET(unix_sock, &master_set);
 	maxfd=unix_sock;
5b532c7f
 	
 	/* listen on the unix socket for the fd */
 	for(;;){
6ee62314
 			timeout.tv_sec=TCP_CHILD_SELECT_TIMEOUT;
 			timeout.tv_usec=0;
6bc40dea
 			sel_set=master_set;
 			nfds=select(maxfd+1, &sel_set, 0 , 0 , &timeout);
cc10bb16
 #ifdef EXTRA_DEBUG
30e8735f
 			for (n=0; n<maxfd; n++){
 				if (FD_ISSET(n, &sel_set)) 
 					DBG("tcp receive: FD %d is set\n", n);
 			}
cc10bb16
 #endif
6bc40dea
 			if (nfds<0){
 				if (errno==EINTR) continue; /* just a signal */
 				/* errors */
 				LOG(L_ERR, "ERROR: tcp_receive_loop: select:(%d) %s\n", errno,
 					strerror(errno));
 				continue;
 			}
 			if (FD_ISSET(unix_sock, &sel_set)){
 				nfds--;
 				/* a new conn from "main" */
 				n=receive_fd(unix_sock, &con, sizeof(con), &s);
 				if (n<0){
 					if (errno == EWOULDBLOCK || errno == EAGAIN ||
 							errno == EINTR){
f535fd9a
 						goto skip;
6bc40dea
 					}else{
 						LOG(L_CRIT,"BUG: tcp_receive_loop: read_fd: %s\n",
5b532c7f
 							strerror(errno));
6bc40dea
 						abort(); /* big error*/
 					}
5b532c7f
 				}
f535fd9a
 				DBG("received n=%d con=%p, fd=%d\n", n, con, s);
6bc40dea
 				if (n==0){
 					LOG(L_ERR, "WARNING: tcp_receive_loop: 0 bytes read\n");
f535fd9a
 					goto skip;
 				}
 				if (con==0){
 					LOG(L_CRIT, "BUG: tcp_receive_loop: null pointer\n");
 					goto skip;
6bc40dea
 				}
6ee62314
 				con->fd=s;
6bc40dea
 				if (s==-1) {
 					LOG(L_ERR, "ERROR: tcp_receive_loop: read_fd:"
5b532c7f
 									"no fd read\n");
0c5da34b
 					resp=CONN_ERROR;
f6e50f08
 					con->state=S_CONN_BAD;
0c5da34b
 					release_tcpconn(con, resp, unix_sock);
f535fd9a
 					goto skip;
6bc40dea
 				}
28427aa4
 #ifdef USE_TLS
 				if (con->type==PROTO_TLS) tls_tcpconn_update_fd(con, s);
 #endif
6ee62314
 				con->timeout=get_ticks()+TCP_CHILD_TIMEOUT;
6bc40dea
 				FD_SET(s, &master_set);
 				if (maxfd<s) maxfd=s;
6ee62314
 				tcpconn_listadd(list, con, c_next, c_prev);
5b532c7f
 			}
f535fd9a
 skip:
6ee62314
 			ticks=get_ticks();
 			for (con=list; con ; con=c_next){
 				c_next=con->c_next; /* safe for removing*/
cc10bb16
 #ifdef EXTRA_DEBUG
30e8735f
 				DBG("tcp receive: list fd=%d, id=%d, timeout=%d, refcnt=%d\n",
 						con->fd, con->id, con->timeout, con->refcnt);
cc10bb16
 #endif
6ee62314
 				if (nfds && FD_ISSET(con->fd, &sel_set)){
cc10bb16
 #ifdef EXTRA_DEBUG
30e8735f
 					DBG("tcp receive: match, fd:isset\n");
cc10bb16
 #endif
6bc40dea
 					nfds--;
0c5da34b
 					resp=tcp_read_req(con);
 					if (resp<0){
6ee62314
 						FD_CLR(con->fd, &master_set);
 						tcpconn_listrm(list, con, c_next, c_prev);
f6e50f08
 						con->state=S_CONN_BAD;
0c5da34b
 						release_tcpconn(con, resp, unix_sock);
6ee62314
 					}else{
 						/* update timeout */
 						con->timeout=ticks+TCP_CHILD_TIMEOUT;
 					}
 				}else{
 					/* timeout */
 					if (con->timeout<=ticks){
 						/* expired, return to "tcp main" */
 						DBG("tcp_receive_loop: %p expired (%d, %d)\n",
 								con, con->timeout, ticks);
0c5da34b
 						resp=CONN_RELEASE;
6ee62314
 						FD_CLR(con->fd, &master_set);
 						tcpconn_listrm(list, con, c_next, c_prev);
0c5da34b
 						release_tcpconn(con, resp, unix_sock);
6ee62314
 					}
 				}
5b532c7f
 			}
 		
 	}
 }
 
 
 #if 0
 int main(int argv, char** argc )
 {
 	printf("starting tests\n");
 	tcp_receive_loop();
 }
 
 #endif
 
 #endif