1 /**************************************************************************************************
2 	$Id: tcp.c,v 1.47 2005/04/20 17:30:38 bboy Exp $
3 
4 	Copyright (C) 2002-2005  Don Moore <bboy@bboy.net>
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at Your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with this program; if not, write to the Free Software
18 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 **************************************************************************************************/
20 
21 #include "named.h"
22 
23 /* Make this nonzero to enable debugging for this source file */
24 #define	DEBUG_TCP	1
25 
26 
27 /**************************************************************************************************
28 	ACCEPT_TCP_QUERY
29 **************************************************************************************************/
30 int
accept_tcp_query(int fd,int family)31 accept_tcp_query(int fd, int family)
32 {
33 	struct sockaddr_in addr4;
34 #if HAVE_IPV6
35 	struct sockaddr_in6 addr6;
36 #endif
37 	socklen_t	addrlen;
38 	int			rmt_fd;
39 	TASK			*t;
40 
41 #if HAVE_IPV6
42 	if (family == AF_INET6)
43 	{
44 	 	addrlen = sizeof(struct sockaddr_in6);
45 		if ((rmt_fd = accept(fd, (struct sockaddr *)&addr6, &addrlen)) < 0)
46 		{
47 			return Warn("%s", _("accept (TCPv6)"));
48 		}
49 		fcntl(rmt_fd, F_SETFL, fcntl(rmt_fd, F_GETFL, 0) | O_NONBLOCK);
50 		if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET6, &addr6)))
51 			return (-1);
52 	}
53 	else
54 #endif
55 	{
56 	 	addrlen = sizeof(struct sockaddr_in);
57 		if ((rmt_fd = accept(fd, (struct sockaddr *)&addr4, &addrlen)) < 0)
58 		{
59 			return Warn("%s", _("accept (TCP)"));
60 		}
61 		fcntl(rmt_fd, F_SETFL, fcntl(rmt_fd, F_GETFL, 0) | O_NONBLOCK);
62 		if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET, &addr4)))
63 			return (-1);
64 	}
65 
66 #if DEBUG_ENABLED && DEBUG_TCP
67 	Debug("%s: TCP connection accepted", clientaddr(t));
68 #endif
69 
70 	return 0;
71 }
72 /*--- accept_tcp_query() ------------------------------------------------------------------------*/
73 
74 
75 /**************************************************************************************************
76 	READ_TCP_LENGTH
77 	The first two octets of a TCP question are the length.  Read them.
78 	Returns 0 on success, -1 on failure.
79 **************************************************************************************************/
80 static int
read_tcp_length(TASK * t)81 read_tcp_length(TASK *t)
82 {
83 	int	rv;
84 	char	len[2];
85 
86 	if ((rv = recv(t->fd, len, 2, 0)) != 2)
87 	{
88 		if (rv < 0)
89 		{
90 			if (errno == EAGAIN)
91 				return (0);
92 			if (errno != ECONNRESET)
93 				Warn("%s: %s", clientaddr(t), _("recv (length) (TCP)"));
94 			return (-1);
95 		}
96 		if (rv == 0)
97 			return (-1);
98 		return Warnx("%s: %s", clientaddr(t), _("TCP message length invalid"));
99 	}
100 
101 	if ((t->len = ((len[0] << 8) | (len[1]))) < DNS_HEADERSIZE)
102 		return Warnx("%s: %s (%d octet%s)", clientaddr(t), _("TCP message too short"), t->len, S(t->len));
103 	if (t->len > DNS_MAXPACKETLEN_TCP)
104 		return Warnx("%s: %s (%d octet%s)", clientaddr(t), _("TCP message too long"), t->len, S(t->len));
105 
106 	if (!(t->query = calloc(1, t->len + 1)))
107 		Err(_("out of memory"));
108 	t->offset = 0;
109 	return (0);
110 }
111 /*--- read_tcp_length() -------------------------------------------------------------------------*/
112 
113 
114 /**************************************************************************************************
115 	READ_TCP_QUERY
116 	Returns 0 on success, -1 on failure.
117 **************************************************************************************************/
118 int
read_tcp_query(TASK * t)119 read_tcp_query(TASK *t)
120 {
121 	unsigned char *end;
122 	int rv;
123 
124 	/* Read packet length if we haven't already */
125 	if (!t->len)
126 		return read_tcp_length(t);
127 
128 	end = t->query + t->len;
129 
130 	/* Read whatever data is ready */
131 	if ((rv = recv(t->fd, t->query + t->offset, t->len - t->offset, 0)) < 0)
132 		return Warn("%s: %s", clientaddr(t), _("recv (TCP)"));
133 	if (!rv)
134 		return (-1);	/* Client closed connection */
135 
136 #if DEBUG_ENABLED && DEBUG_TCP
137 	Debug("%s: 2+%d TCP octets in", clientaddr(t), rv);
138 #endif
139 
140 	t->offset += rv;
141 	if (t->offset > t->len)
142 		return Warnx("%s: %s", clientaddr(t), _("TCP message data too long"));
143 	if (t->offset < t->len)
144 		return 0;													/* Not finished reading */
145 	t->offset = 0;													/* Reset offset for writing reply */
146 
147 	return new_task(t, t->query, t->len);
148 }
149 /*--- read_tcp_query() --------------------------------------------------------------------------*/
150 
151 
152 /**************************************************************************************************
153 	WRITE_TCP_LENGTH
154 	Writes the length octets for TCP reply.  Returns 0 on success, -1 on failure.
155 **************************************************************************************************/
156 static int
write_tcp_length(TASK * t)157 write_tcp_length(TASK *t)
158 {
159 	char	len[2], *l;
160 	int	rv;
161 
162 	l = len;
163 	DNS_PUT16(l, t->replylen);
164 
165 	if ((rv = write(t->fd, len + t->offset, SIZE16 - t->offset)) < 0)
166 	{
167 		if (errno == EINTR)
168 			return 0;
169 		return Warn("%s: %s", clientaddr(t), _("write (length) (TCP)"));
170 	}
171 	if (!rv)
172 		return (-1);		/* Client closed connection */
173 	t->offset += rv;
174 	if (t->offset >= SIZE16)
175 	{
176 		t->len_written = 1;
177 		t->offset = 0;
178 	}
179 	return 0;
180 }
181 /*--- write_tcp_length() ------------------------------------------------------------------------*/
182 
183 
184 /**************************************************************************************************
185 	WRITE_TCP_REPLY
186 	Returns 0 on success, -1 on error.  If -1 is returned, the task is no longer valid.
187 **************************************************************************************************/
188 int
write_tcp_reply(TASK * t)189 write_tcp_reply(TASK *t)
190 {
191 	int rv, rmt_fd;
192 	struct sockaddr_in addr4;
193 #if HAVE_IPV6
194 	struct sockaddr_in6 addr6;
195 #endif
196 
197 	/* Write TCP length if we haven't already */
198 	if (!t->len_written)
199 	{
200 		if (write_tcp_length(t) < 0)
201 		{
202 			dequeue(Tasks, t);
203 			return (-1);
204 		}
205 		return (0);
206 	}
207 
208 	/* Write the reply */
209 	if ((rv = write(t->fd, t->reply + t->offset, t->replylen - t->offset)) < 0)
210 	{
211 		if (errno == EINTR)
212 			return (0);
213 		dequeue(Tasks, t);
214 		return Warn("%s: %s", clientaddr(t), _("write (TCP)"));
215 	}
216 	if (!rv)
217 	{
218 		dequeue(Tasks, t);
219 		return (-1);												/* Client closed connection */
220 	}
221 	t->offset += rv;
222 	if (t->offset < t->replylen)
223 		return (0);													/* Not finished yet... */
224 
225 	/* Task complete; reset.  The TCP client must be able to perform multiple queries on
226 		the same connection (BIND8 AXFR does this for sure) */
227 #if HAVE_IPV6
228 	if (t->family == AF_INET6)
229 	{
230 		memcpy(&addr6, &t->addr6, sizeof(struct sockaddr_in6));
231 		rmt_fd = t->fd;
232 		dequeue(Tasks, t);
233 
234 		/* Reinitialize to allow multiple queries on TCP */
235 		if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET6, &addr6)))
236 			return (-2);
237 	}
238 	else
239 #endif
240 	{
241 		memcpy(&addr4, &t->addr4, sizeof(struct sockaddr_in));
242 		rmt_fd = t->fd;
243 		dequeue(Tasks, t);
244 
245 		/* Reinitialize to allow multiple queries on TCP */
246 		if (!(t = task_init(NEED_READ, rmt_fd, SOCK_STREAM, AF_INET, &addr4)))
247 			return (-2);
248 	}
249 	return (0);
250 }
251 /*--- write_tcp_reply() -------------------------------------------------------------------------*/
252 
253 /* vi:set ts=3: */
254 /* NEED_PO */
255