proto_tcp.c revision 204076
1103856Stjr/*-
2103856Stjr * Copyright (c) 2009-2010 The FreeBSD Foundation
3103856Stjr * All rights reserved.
4103856Stjr *
5227753Stheraven * This software was developed by Pawel Jakub Dawidek under sponsorship from
6227753Stheraven * the FreeBSD Foundation.
7227753Stheraven *
8227753Stheraven * Redistribution and use in source and binary forms, with or without
9227753Stheraven * modification, are permitted provided that the following conditions
10103856Stjr * are met:
11103856Stjr * 1. Redistributions of source code must retain the above copyright
12103856Stjr *    notice, this list of conditions and the following disclaimer.
13103856Stjr * 2. Redistributions in binary form must reproduce the above copyright
14103856Stjr *    notice, this list of conditions and the following disclaimer in the
15103856Stjr *    documentation and/or other materials provided with the distribution.
16103856Stjr *
17103856Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18103856Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19103856Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20103856Stjr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21103856Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22103856Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23103856Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24103856Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25103856Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26103856Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27103856Stjr * SUCH DAMAGE.
28103856Stjr */
29103856Stjr
30103856Stjr#include <sys/cdefs.h>
31103856Stjr__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp4.c 204076 2010-02-18 23:16:19Z pjd $");
32103856Stjr
33103856Stjr#include <sys/param.h>	/* MAXHOSTNAMELEN */
34103856Stjr
35103856Stjr#include <netinet/in.h>
36103856Stjr#include <netinet/tcp.h>
37103856Stjr
38227753Stheraven#include <assert.h>
39103856Stjr#include <errno.h>
40103856Stjr#include <netdb.h>
41103856Stjr#include <stdbool.h>
42103856Stjr#include <stdint.h>
43103856Stjr#include <stdio.h>
44103856Stjr#include <string.h>
45227753Stheraven#include <unistd.h>
46227753Stheraven
47227753Stheraven#include "hast.h"
48227753Stheraven#include "pjdlog.h"
49227753Stheraven#include "proto_impl.h"
50
51#define	TCP4_CTX_MAGIC	0x7c441c
52struct tcp4_ctx {
53	int			tc_magic;
54	struct sockaddr_in	tc_sin;
55	int			tc_fd;
56	int			tc_side;
57#define	TCP4_SIDE_CLIENT	0
58#define	TCP4_SIDE_SERVER_LISTEN	1
59#define	TCP4_SIDE_SERVER_WORK	2
60};
61
62static void tcp4_close(void *ctx);
63
64static in_addr_t
65str2ip(const char *str)
66{
67	struct hostent *hp;
68	in_addr_t ip;
69
70	ip = inet_addr(str);
71	if (ip != INADDR_NONE) {
72		/* It is a valid IP address. */
73		return (ip);
74	}
75	/* Check if it is a valid host name. */
76	hp = gethostbyname(str);
77	if (hp == NULL)
78		return (INADDR_NONE);
79	return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
80}
81
82/*
83 * Function converts the given string to unsigned number.
84 */
85static int
86numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
87{
88	intmax_t digit, num;
89
90	if (str[0] == '\0')
91		goto invalid;	/* Empty string. */
92	num = 0;
93	for (; *str != '\0'; str++) {
94		if (*str < '0' || *str > '9')
95			goto invalid;	/* Non-digit character. */
96		digit = *str - '0';
97		if (num > num * 10 + digit)
98			goto invalid;	/* Overflow. */
99		num = num * 10 + digit;
100		if (num > maxnum)
101			goto invalid;	/* Too big. */
102	}
103	if (num < minnum)
104		goto invalid;	/* Too small. */
105	*nump = num;
106	return (0);
107invalid:
108	errno = EINVAL;
109	return (-1);
110}
111
112static int
113tcp4_addr(const char *addr, struct sockaddr_in *sinp)
114{
115	char iporhost[MAXHOSTNAMELEN];
116	const char *pp;
117	size_t size;
118	in_addr_t ip;
119
120	if (addr == NULL)
121		return (-1);
122
123	if (strncasecmp(addr, "tcp4://", 7) == 0)
124		addr += 7;
125	else if (strncasecmp(addr, "tcp://", 6) == 0)
126		addr += 6;
127	else if (addr[0] != '/' &&	/* If this is not path... */
128	    strstr(addr, "://") == NULL)/* ...and has no prefix... */
129		;			/* ...tcp4 is the default. */
130	else
131		return (-1);
132
133	sinp->sin_family = AF_INET;
134	sinp->sin_len = sizeof(*sinp);
135	/* Extract optional port. */
136	pp = strrchr(addr, ':');
137	if (pp == NULL) {
138		/* Port not given, use the default. */
139		sinp->sin_port = htons(HASTD_PORT);
140	} else {
141		intmax_t port;
142
143		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
144			return (errno);
145		sinp->sin_port = htons(port);
146	}
147	/* Extract host name or IP address. */
148	if (pp == NULL) {
149		size = sizeof(iporhost);
150		if (strlcpy(iporhost, addr, size) >= size)
151			return (ENAMETOOLONG);
152	} else {
153		size = (size_t)(pp - addr + 1);
154		if (size > sizeof(iporhost))
155			return (ENAMETOOLONG);
156		strlcpy(iporhost, addr, size);
157	}
158	/* Convert string (IP address or host name) to in_addr_t. */
159	ip = str2ip(iporhost);
160	if (ip == INADDR_NONE)
161		return (EINVAL);
162	sinp->sin_addr.s_addr = ip;
163
164	return (0);
165}
166
167static int
168tcp4_common_setup(const char *addr, void **ctxp, int side)
169{
170	struct tcp4_ctx *tctx;
171	int ret, val;
172
173	tctx = malloc(sizeof(*tctx));
174	if (tctx == NULL)
175		return (errno);
176
177	/* Parse given address. */
178	if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) {
179		free(tctx);
180		return (ret);
181	}
182
183	tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
184	if (tctx->tc_fd == -1) {
185		ret = errno;
186		free(tctx);
187		return (ret);
188	}
189
190	/* Socket settings. */
191	val = 1;
192	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &val,
193	    sizeof(val)) == -1) {
194		pjdlog_warning("Unable to set TCP_NOELAY on %s", addr);
195	}
196	val = 131072;
197	if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_SNDBUF, &val,
198	    sizeof(val)) == -1) {
199		pjdlog_warning("Unable to set send buffer size on %s", addr);
200	}
201	val = 131072;
202	if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_RCVBUF, &val,
203	    sizeof(val)) == -1) {
204		pjdlog_warning("Unable to set receive buffer size on %s", addr);
205	}
206
207	tctx->tc_side = side;
208	tctx->tc_magic = TCP4_CTX_MAGIC;
209	*ctxp = tctx;
210
211	return (0);
212}
213
214static int
215tcp4_client(const char *addr, void **ctxp)
216{
217
218	return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
219}
220
221static int
222tcp4_connect(void *ctx)
223{
224	struct tcp4_ctx *tctx = ctx;
225
226	assert(tctx != NULL);
227	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
228	assert(tctx->tc_side == TCP4_SIDE_CLIENT);
229	assert(tctx->tc_fd >= 0);
230
231	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
232	    sizeof(tctx->tc_sin)) < 0) {
233		return (errno);
234	}
235
236	return (0);
237}
238
239static int
240tcp4_server(const char *addr, void **ctxp)
241{
242	struct tcp4_ctx *tctx;
243	int ret, val;
244
245	ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN);
246	if (ret != 0)
247		return (ret);
248
249	tctx = *ctxp;
250
251	val = 1;
252	/* Ignore failure. */
253	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
254	   sizeof(val));
255
256	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
257	    sizeof(tctx->tc_sin)) < 0) {
258		ret = errno;
259		tcp4_close(tctx);
260		return (ret);
261	}
262	if (listen(tctx->tc_fd, 8) < 0) {
263		ret = errno;
264		tcp4_close(tctx);
265		return (ret);
266	}
267
268	return (0);
269}
270
271static int
272tcp4_accept(void *ctx, void **newctxp)
273{
274	struct tcp4_ctx *tctx = ctx;
275	struct tcp4_ctx *newtctx;
276	socklen_t fromlen;
277	int ret;
278
279	assert(tctx != NULL);
280	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
281	assert(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN);
282	assert(tctx->tc_fd >= 0);
283
284	newtctx = malloc(sizeof(*newtctx));
285	if (newtctx == NULL)
286		return (errno);
287
288	fromlen = sizeof(tctx->tc_sin);
289	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
290	    &fromlen);
291	if (newtctx->tc_fd < 0) {
292		ret = errno;
293		free(newtctx);
294		return (ret);
295	}
296
297	newtctx->tc_side = TCP4_SIDE_SERVER_WORK;
298	newtctx->tc_magic = TCP4_CTX_MAGIC;
299	*newctxp = newtctx;
300
301	return (0);
302}
303
304static int
305tcp4_send(void *ctx, const unsigned char *data, size_t size)
306{
307	struct tcp4_ctx *tctx = ctx;
308
309	assert(tctx != NULL);
310	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
311	assert(tctx->tc_fd >= 0);
312
313	return (proto_common_send(tctx->tc_fd, data, size));
314}
315
316static int
317tcp4_recv(void *ctx, unsigned char *data, size_t size)
318{
319	struct tcp4_ctx *tctx = ctx;
320
321	assert(tctx != NULL);
322	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
323	assert(tctx->tc_fd >= 0);
324
325	return (proto_common_recv(tctx->tc_fd, data, size));
326}
327
328static int
329tcp4_descriptor(const void *ctx)
330{
331	const struct tcp4_ctx *tctx = ctx;
332
333	assert(tctx != NULL);
334	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
335
336	return (tctx->tc_fd);
337}
338
339static void
340sin2str(struct sockaddr_in *sinp, char *addr, size_t size)
341{
342	in_addr_t ip;
343	unsigned int port;
344
345	assert(addr != NULL);
346	assert(sinp->sin_family == AF_INET);
347
348	ip = ntohl(sinp->sin_addr.s_addr);
349	port = ntohs(sinp->sin_port);
350	snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u", ((ip >> 24) & 0xff),
351	    ((ip >> 16) & 0xff), ((ip >> 8) & 0xff), (ip & 0xff), port);
352}
353
354static bool
355tcp4_address_match(const void *ctx, const char *addr)
356{
357	const struct tcp4_ctx *tctx = ctx;
358	struct sockaddr_in sin;
359	socklen_t sinlen;
360	in_addr_t ip1, ip2;
361
362	assert(tctx != NULL);
363	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
364
365	if (tcp4_addr(addr, &sin) != 0)
366		return (false);
367	ip1 = sin.sin_addr.s_addr;
368
369	sinlen = sizeof(sin);
370	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
371		return (false);
372	ip2 = sin.sin_addr.s_addr;
373
374	return (ip1 == ip2);
375}
376
377static void
378tcp4_local_address(const void *ctx, char *addr, size_t size)
379{
380	const struct tcp4_ctx *tctx = ctx;
381	struct sockaddr_in sin;
382	socklen_t sinlen;
383
384	assert(tctx != NULL);
385	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
386
387	sinlen = sizeof(sin);
388	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
389		strlcpy(addr, "N/A", size);
390		return;
391	}
392	sin2str(&sin, addr, size);
393}
394
395static void
396tcp4_remote_address(const void *ctx, char *addr, size_t size)
397{
398	const struct tcp4_ctx *tctx = ctx;
399	struct sockaddr_in sin;
400	socklen_t sinlen;
401
402	assert(tctx != NULL);
403	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
404
405	sinlen = sizeof(sin);
406	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
407		strlcpy(addr, "N/A", size);
408		return;
409	}
410	sin2str(&sin, addr, size);
411}
412
413static void
414tcp4_close(void *ctx)
415{
416	struct tcp4_ctx *tctx = ctx;
417
418	assert(tctx != NULL);
419	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
420
421	if (tctx->tc_fd >= 0)
422		close(tctx->tc_fd);
423	tctx->tc_magic = 0;
424	free(tctx);
425}
426
427static struct hast_proto tcp4_proto = {
428	.hp_name = "tcp4",
429	.hp_client = tcp4_client,
430	.hp_connect = tcp4_connect,
431	.hp_server = tcp4_server,
432	.hp_accept = tcp4_accept,
433	.hp_send = tcp4_send,
434	.hp_recv = tcp4_recv,
435	.hp_descriptor = tcp4_descriptor,
436	.hp_address_match = tcp4_address_match,
437	.hp_local_address = tcp4_local_address,
438	.hp_remote_address = tcp4_remote_address,
439	.hp_close = tcp4_close
440};
441
442static __constructor void
443tcp4_ctor(void)
444{
445
446	proto_register(&tcp4_proto);
447}
448