/****************************************************************************
 * Copyright (C) 1998 WIDE Project. All rights reserved.
 * Copyright (C) 1999,2000,2001,2002 University of Tromso. All rights reserved.
 * Copyright (C) 2002 Invenia Innovation AS. All rights reserved.
 *
 * Author: Feike W. Dillema, feico@pasta.cs.uit.no.
 *         based on newbie code by Yusuke DOI, Keio Univ. Murai Lab.
 ****************************************************************************/


/*
 * <$Id: tcp_response.c,v 3.52 2005/07/04 09:09:22 dillema Exp $>
 */

#include "totd.h"

int tcp_response_start (int sock, struct sockaddr *sa_p) {
	const char *fn = "tcp_response_start()";
	Context *cont;

	syslog (LOG_DEBUG, "%s: start", fn);

	cont = context_create();
	if (!cont)
		return (response_abort (cont, -1));

	cont->process = tcp_response_readlen_process;
	cont->retry = tcp_response_readlen_retry;
	memcpy (cont->peer, sa_p, SOCKADDR_SIZEOF(*sa_p));

	cont->conn_sock = sock;
	cont->timeout = TCP_SRV_TIMEOUT;

	if (!context_timeout_register (cont, cont->timeout) &&
	    !ev_tcp_conn_in_register (cont->conn_sock, cont))
			return 0; /* SUCCESS */

	return (response_abort (cont, -1));
}

int tcp_response_readlen_process (Context *cont) {
	const char *fn = "tcp_response_readlen_process()";
	uint16_t length_buf;

	syslog (LOG_DEBUG, "%s: start", fn);

	/* renew timeout */
	if (cont->tout)
		cont->tout->handler = NULL;

	if (context_timeout_register (cont, cont->timeout))
		return (response_abort (cont, -1));

	/* still use old input list -- data following */

	/* read length buffer */
	if (read (cont->conn_sock, (u_char *) (&length_buf),
	    sizeof (uint16_t)) < sizeof (uint16_t)) {
		syslog (LOG_NOTICE, "%s: cannot read length TCP message", fn);
		return (response_abort (cont, -1));
	}

	length_buf = ntohs (length_buf);
	syslog (LOG_DEBUG, "%s: data length = %d", fn, length_buf);

	/* free old buffer */
	if (cont->mesg.p)
		free (cont->mesg.p);

	/* allocate new properly sized buffer */
	cont->mesg.p = malloc (length_buf);
	if (!cont->mesg.p)
		return (response_abort (cont, -1));

	cont->mesg_len = length_buf;
	cont->wp = cont->mesg.p;

	/* go next state */
	cont->process = tcp_response_reading_process;
	cont->retry = tcp_response_reading_retry;

	/* SUCCESS */
	return 0;
}

int tcp_response_readlen_retry (Context *cont) {
	const char *fn = "tcp_response_readlen_retry()";

	syslog (LOG_NOTICE, "%s: connection does not respond. closing.", fn);
	return (response_abort (cont, -1));
}

int tcp_response_reading_process (Context *cont) {
	const char *fn = "tcp_response_reading_process()";
	int len;

	syslog (LOG_DEBUG, "%s: start", fn);

	if (cont->tout)
		cont->tout->handler = NULL;

	if (context_timeout_register (cont, cont->timeout))
		return(response_abort (cont, -1));

	len = read (cont->conn_sock, cont->wp,
		    cont->mesg_len - (cont->wp - cont->mesg.p));

	if (len <= 0)
		return(response_abort (cont, -1));

	cont->wp += len;
	if (cont->wp < (cont->mesg.p + cont->mesg_len)) {
		syslog (LOG_DEBUG, "%s: left %td bytes -- continue.", fn,
			(cont->mesg.p + cont->mesg_len) - cont->wp);

		/* SUCCESS */
		return 0;	/* the processing continues ... */
	}

	/* all data has been read now */
	ev_tcp_conn_in_remove (cont->conn_sock);

	if (cont->mesg.hdr->opcode == OP_QUERY) {
		if (cont->tout)
			cont->tout->handler = NULL;

		/*
		 * don't register input list,
		 * child context takes input
		 */

		switch (request_start (cont, QUERY_TCP)) {
		case 0:
			/*
			 * We got a response
			 * goto recursive_processing state
			 * clear events, child will call me then
			 */
			if (cont->tout)
				cont->tout->handler = NULL;

			cont->process = tcp_response_recursive_process;
			cont->retry = tcp_response_recursive_retry;

			/* SUCCESS */
			return 0;
		case 1:
			/* Something wrong with the query */
			syslog (LOG_NOTICE, "%s: Request failed", fn);
			cont->mesg.hdr->rcode = RC_FMTERR;
		default:
			/* totd failed itself somehow */
			syslog (LOG_NOTICE, "%s: Totd failed", fn);
			cont->mesg.hdr->rcode = RC_SERVERERR;
		}
	} else
		cont->mesg.hdr->rcode = RC_NIMP;

	/* 
	 * Not OP_QUERY or Request failed brings us here
	 *
	 * Go to writing state. Timeout already done in this function
	 * Register new output event 
	 */

	if (!ev_tcp_out_register (cont->conn_sock, cont)) {
		assemble_response (cont);

		/* state change */
		cont->process = tcp_response_writing_process;
		cont->retry = tcp_response_writing_retry;
		cont->wp = NULL;

		syslog (LOG_DEBUG, "%s: end (writing:)", fn);
		/* SUCCESS */
		return 0;
	}

	/* FAILURE */
	return (response_abort (cont, -1));
}

int tcp_response_reading_retry (Context *cont) {
	return (response_abort (cont, -1));
}

int tcp_response_recursive_process (Context *cont) {
	const char *fn = "tcp_response_recursive_process()";

	syslog (LOG_DEBUG, "%s: start", fn);

	switch (recursive_process (cont)) {
	case 0:
		syslog (LOG_DEBUG, "%s: return, continue", fn);

		/* SUCCESS */
		return 0;
	case 1:
		assemble_response (cont);

		if (cont->mesg_len < 0 || cont->mesg_len > MAX_STREAM) {
			syslog (LOG_WARNING, "response message too big");
			return (response_abort (cont, 1));
		}

		if (cont->tout)
			cont->tout->handler = NULL;

		if (context_timeout_register (cont, cont->timeout))
			return (response_abort (cont, -1));

		/* register output */
		if (ev_tcp_out_register (cont->conn_sock, cont))
			return (response_abort (cont, -1));

		/* change the state */
		cont->process = tcp_response_writing_process;
		cont->retry = tcp_response_writing_retry;
		cont->wp = NULL;

		syslog (LOG_DEBUG, "%s: return, finish", fn);

		/* SUCCESS */
		return 0;
	default:
		return (response_abort (cont, -1));
	}
}

int tcp_response_recursive_retry (Context *cont) {
	return (response_abort (cont, -1));
}

int tcp_response_writing_process (Context *cont) {
	const char *fn = "tcp_response_writing_process()";

	syslog (LOG_DEBUG, "%s: start", fn);

	/* renew timeout */
	if (cont->tout)
		cont->tout->handler = NULL;

	if (context_timeout_register (cont, cont->timeout))
		return (response_abort (cont, -1));

	switch (tcp_writemesg (cont, cont->conn_sock)) {
	case 0:
		/* continue this process */
		syslog (LOG_DEBUG, "%s: return, continue", fn);

		/* SUCCESS */
		return 0;
	case 1:
		/* all data written */
		cont->process = tcp_response_waiting_client_close_process;
		cont->retry = tcp_response_waiting_client_close_retry;

		/* stop output and waiting for input */
		ev_tcp_out_remove (cont->conn_sock);
		ev_tcp_conn_in_register (cont->conn_sock, cont);

		syslog (LOG_DEBUG, "%s: return, finish", fn);

		/* SUCCESS */
		return 0;
	default:
		/* FAILURE */
		return (response_abort (cont, -1));
	}
}

int tcp_response_writing_retry (Context *cont) {
	return (response_abort (cont, -1));
}

int tcp_response_waiting_client_close_process (Context *cont) {
	const char *fn = "tcp_response_waiting_client_close_process()";
	uint16_t length_buf;
	int i;

	syslog (LOG_DEBUG, "%s: start", fn);

	i = read (cont->conn_sock, (u_char *) (&length_buf),
		  sizeof (uint16_t));
	if (!i) /* SUCCESS */
		return tcp_response_finish (cont);

	if (i < sizeof (uint16_t)) {
		/* read error */
		syslog (LOG_INFO, "%s: read(): %m", fn);
		return (response_abort (cont, -1));
	}

	/* redo query processing */
	length_buf = ntohs (length_buf);
	syslog (LOG_DEBUG, "%s: incoming length %d", fn, length_buf);

	/* free old buffer */
	if (cont->mesg.p)
		free (cont->mesg.p);

	/* allocate new buffer */
	cont->mesg.p = malloc (length_buf);
	if (!cont->mesg.p) {
		cont->mesg_len = 0;
		return (response_abort (cont, -1));
	}

	cont->mesg_len = length_buf;
	cont->wp = cont->mesg.p;
	cont->timeout = TCP_SRV_TIMEOUT;

	/* renew timeout */
	if (cont->tout)
		cont->tout->handler = NULL;

	if (context_timeout_register (cont, cont->timeout))
		return (response_abort (cont, -1));

	/*
	 * I/O event is not changed -- tcp_conn_in,
	 * go back to reading
	 */

	cont->process = tcp_response_reading_process;
	cont->retry = tcp_response_reading_retry;

	syslog (LOG_DEBUG, "%s: return, continue reading", fn);

	/* SUCCESS, but not FINISHED yet */
	return 0;
}

int tcp_response_waiting_client_close_retry (Context *cont) {
	return (response_abort (cont, 0));
}

int tcp_response_finish (Context *cont) {

	syslog (LOG_DEBUG, "tcp_response_finish()");

	context_destroy (cont);
	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1