1 /*
2 * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3 * Copyright (C) 2007-2013 Sourcefire, Inc.
4 *
5 * Authors: Tomasz Kojm, Török Edvin
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
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., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #if HAVE_CONFIG_H
23 #include "clamav-config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifndef _WIN32
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <netdb.h>
37 #endif
38 #include <errno.h>
39
40 // libclamav
41 #include "clamav.h"
42
43 // common
44 #include "optparser.h"
45 #include "output.h"
46 #include "misc.h"
47
48 #include "clamd_others.h"
49 #include "server.h"
50 #include "tcpserver.h"
51
tcpserver(int ** lsockets,unsigned int * nlsockets,char * ipaddr,const struct optstruct * opts)52 int tcpserver(int **lsockets, unsigned int *nlsockets, char *ipaddr, const struct optstruct *opts)
53 {
54 struct addrinfo hints, *info, *p;
55 char host[NI_MAXHOST], serv[NI_MAXSERV];
56 int *sockets;
57 int sockfd = 0, backlog;
58 int *t;
59 char *estr, port[10];
60 int yes = 1;
61 int res;
62 unsigned int i = 0;
63 int num_fd;
64
65 sockets = *lsockets;
66
67 num_fd = sd_listen_fds(0);
68 if (num_fd > 2) {
69 logg("!TCP: Received more than two file descriptors from systemd.\n");
70 return -1;
71 } else if (num_fd > 0) {
72 /* use socket passed by systemd */
73 int i;
74 for (i = 0; i < num_fd; i += 1) {
75 sockfd = SD_LISTEN_FDS_START + i;
76 if (sd_is_socket(sockfd, AF_INET, SOCK_STREAM, 1) == 1) {
77 /* correct socket */
78 logg("#TCP: Received AF_INET SOCK_STREAM socket from systemd.\n");
79 break;
80 } else if (sd_is_socket(sockfd, AF_INET6, SOCK_STREAM, 1) == 1) {
81 /* correct socket */
82 logg("#TCP: Received AF_INET6 SOCK_STREAM socket from systemd.\n");
83 break;
84 } else {
85 /* wrong socket */
86 sockfd = -2;
87 }
88 }
89 if (sockfd == -2) {
90 logg("#TCP: No tcp AF_INET/AF_INET6 SOCK_STREAM socket received from systemd.\n");
91 return -2;
92 }
93
94 t = realloc(sockets, sizeof(int) * (*nlsockets + 1));
95 if (!(t)) {
96 return -1;
97 }
98 sockets = t;
99
100 sockets[*nlsockets] = sockfd;
101 (*nlsockets)++;
102 *lsockets = sockets;
103 return 0;
104 }
105
106 /* create socket */
107 snprintf(port, sizeof(port), "%lld", optget(opts, "TCPSocket")->numarg);
108
109 memset(&hints, 0x00, sizeof(struct addrinfo));
110 hints.ai_family = AF_UNSPEC;
111 hints.ai_socktype = SOCK_STREAM;
112 hints.ai_flags = AI_PASSIVE;
113
114 #ifdef AI_ADDRCONFIG
115 hints.ai_flags |= AI_ADDRCONFIG;
116 #endif
117
118 if ((res = getaddrinfo(ipaddr, port, &hints, &info))) {
119 logg("!TCP: getaddrinfo failed: %s\n", gai_strerror(res));
120 return -1;
121 }
122
123 for (p = info; p != NULL; p = p->ai_next, i++) {
124 t = realloc(sockets, sizeof(int) * (*nlsockets + 1));
125 if (!(t)) {
126 for (i = 0; i < *nlsockets; i++)
127 close(sockets[i]);
128
129 freeaddrinfo(info);
130 return -1;
131 }
132 sockets = t;
133
134 if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
135 estr = strerror(errno);
136 logg("!TCP: socket() error: %s\n", estr);
137 continue;
138 }
139
140 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)) == -1) {
141 logg("!TCP: setsocktopt(SO_REUSEADDR) error: %s\n", strerror(errno));
142 }
143
144 #ifdef IPV6_V6ONLY
145 if (p->ai_family == AF_INET6 &&
146 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1) {
147 estr = strerror(errno);
148 logg("!TCP: setsocktopt(IPV6_V6ONLY) error: %s\n", estr);
149 }
150 #endif /* IPV6_V6ONLY */
151
152 #ifdef HAVE_GETNAMEINFO
153 if ((res = getnameinfo(p->ai_addr, p->ai_addrlen, host, sizeof(host),
154 serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV))) {
155 logg("!TCP: getnameinfo failed: %s\n", gai_strerror(res));
156 host[0] = '\0';
157 serv[0] = '\0';
158 }
159 #else
160 if (ipaddr) {
161 strncpy(host, ipaddr, sizeof(host));
162 host[sizeof(host) - 1] = '\0';
163 } else
164 host[0] = '\0';
165 snprintf(serv, sizeof(serv), "%u", (unsigned int)(optget(opts, "TCPSocket")->numarg));
166 #endif
167 if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
168 estr = strerror(errno);
169 logg("!TCP: Cannot bind to [%s]:%s: %s\n", host, serv, estr);
170 closesocket(sockfd);
171
172 continue;
173 }
174 logg("#TCP: Bound to [%s]:%s\n", host, serv);
175
176 backlog = optget(opts, "MaxConnectionQueueLength")->numarg;
177 logg("#TCP: Setting connection queue length to %d\n", backlog);
178
179 if (listen(sockfd, backlog) == -1) {
180 estr = strerror(errno);
181 logg("!TCP: Cannot listen on [%s]:%s: %s\n", host, serv, estr);
182 closesocket(sockfd);
183
184 continue;
185 }
186
187 sockets[*nlsockets] = sockfd;
188 (*nlsockets)++;
189 }
190
191 freeaddrinfo(info);
192 *lsockets = sockets;
193
194 return 0;
195 }
196