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