]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sbin/hastd/proto_tcp4.c
MFC r204076,r204077,r204083,r205279:
[FreeBSD/stable/8.git] / sbin / hastd / proto_tcp4.c
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>  /* MAXHOSTNAMELEN */
34
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37
38 #include <assert.h>
39 #include <errno.h>
40 #include <netdb.h>
41 #include <stdbool.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "hast.h"
48 #include "pjdlog.h"
49 #include "proto_impl.h"
50
51 #define TCP4_CTX_MAGIC  0x7c441c
52 struct 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
62 static void tcp4_close(void *ctx);
63
64 static in_addr_t
65 str2ip(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  */
85 static int
86 numfromstr(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);
107 invalid:
108         errno = EINVAL;
109         return (-1);
110 }
111
112 static int
113 tcp4_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
167 static int
168 tcp4_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
214 static int
215 tcp4_client(const char *addr, void **ctxp)
216 {
217
218         return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
219 }
220
221 static int
222 tcp4_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
239 static int
240 tcp4_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
271 static int
272 tcp4_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
304 static int
305 tcp4_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
316 static int
317 tcp4_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
328 static int
329 tcp4_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
339 static void
340 sin2str(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
354 static bool
355 tcp4_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
377 static void
378 tcp4_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
395 static void
396 tcp4_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
413 static void
414 tcp4_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
427 static 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
442 static __constructor void
443 tcp4_ctor(void)
444 {
445
446         proto_register(&tcp4_proto);
447 }