Branch data Line data Source code
1 : : /*
2 : : *****************************************************************************
3 : : *
4 : : * File: tcp_server.c
5 : : *
6 : : * Purpose: Spawns off a dummy tcp server for fwknopd. Its purpose is
7 : : * to accept a tcp connection, then drop it after the first packet.
8 : : *
9 : : * Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
10 : : * Copyright (C) 2009-2014 fwknop developers and contributors. For a full
11 : : * list of contributors, see the file 'CREDITS'.
12 : : *
13 : : * License (GNU General Public License):
14 : : *
15 : : * This program is free software; you can redistribute it and/or
16 : : * modify it under the terms of the GNU General Public License
17 : : * as published by the Free Software Foundation; either version 2
18 : : * of the License, or (at your option) any later version.
19 : : *
20 : : * This program is distributed in the hope that it will be useful,
21 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 : : * GNU General Public License for more details.
24 : : *
25 : : * You should have received a copy of the GNU General Public License
26 : : * along with this program; if not, write to the Free Software
27 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 : : * USA
29 : : *
30 : : *****************************************************************************
31 : : */
32 : : #include "fwknopd_common.h"
33 : : #include "tcp_server.h"
34 : : #include "log_msg.h"
35 : : #include "utils.h"
36 : : #include <errno.h>
37 : :
38 : : #if HAVE_SYS_SOCKET_H
39 : : #include <sys/socket.h>
40 : : #endif
41 : : #if HAVE_ARPA_INET_H
42 : : #include <arpa/inet.h>
43 : : #endif
44 : : #if HAVE_NETDB
45 : : #include <netdb.h>
46 : : #endif
47 : :
48 : : #include <fcntl.h>
49 : : #include <sys/select.h>
50 : :
51 : : /* Fork off and run a "dummy" TCP server. The return value is the PID of
52 : : * the child process or -1 if there is a fork error.
53 : : */
54 : : int
55 : 1 : run_tcp_server(fko_srv_options_t *opts)
56 : : {
57 : : #if !CODE_COVERAGE
58 : : pid_t pid, ppid;
59 : : #endif
60 : : int s_sock, c_sock, sfd_flags, clen, selval;
61 : 1 : int reuse_addr = 1, is_err, rv=1;
62 : : fd_set sfd_set;
63 : : struct sockaddr_in saddr, caddr;
64 : : struct timeval tv;
65 : 1 : char sipbuf[MAX_IPV4_STR_LEN] = {0};
66 : :
67 : : unsigned short port;
68 : :
69 : 1 : port = strtol_wrapper(opts->config[CONF_TCPSERV_PORT],
70 : : 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
71 [ - + ]: 1 : if(is_err != FKO_SUCCESS)
72 : : {
73 : 0 : log_msg(LOG_ERR, "[*] Invalid max TCPSERV_PORT value.");
74 : 0 : return -1;
75 : : }
76 : 1 : log_msg(LOG_INFO, "Kicking off TCP server to listen on port %i.", port);
77 : :
78 : : #if !CODE_COVERAGE
79 : : /* Fork off a child process to run the command and provide its outputs.
80 : : */
81 : : pid = fork();
82 : :
83 : : /* Non-zero pid means we are the parent or there was a fork error.
84 : : * in either case we simply return that value to the caller.
85 : : */
86 : : if (pid != 0)
87 : : {
88 : : opts->tcp_server_pid = pid;
89 : : return(pid);
90 : : }
91 : :
92 : : /* Get our parent PID so we can periodically check for it. We want to
93 : : * know when it goes away so we can too.
94 : : */
95 : : ppid = getppid();
96 : :
97 : : /* We are the child. The first thing to do is close our copy of the
98 : : * parent PID file so we don't end up holding the lock if the parent
99 : : * suffers a sudden death that doesn't take us out too.
100 : : */
101 : : close(opts->lock_fd);
102 : : #endif
103 : :
104 : : /* Now, let's make a TCP server
105 : : */
106 [ - + ]: 1 : if ((s_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
107 : : {
108 : 0 : log_msg(LOG_ERR, "run_tcp_server: socket() failed: %s",
109 : 0 : strerror(errno));
110 : 0 : return -1;
111 : : }
112 : :
113 : : /* So that we can re-bind to it without TIME_WAIT problems
114 : : */
115 [ - + ]: 1 : if(setsockopt(s_sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1)
116 : : {
117 : 0 : log_msg(LOG_ERR, "run_tcp_server: setsockopt error: %s",
118 : 0 : strerror(errno));
119 : 0 : close(s_sock);
120 : 0 : return -1;
121 : : }
122 : :
123 : : /* Make our main socket non-blocking so we don't have to be stuck on
124 : : * listening for incoming connections.
125 : : */
126 [ - + ]: 1 : if((sfd_flags = fcntl(s_sock, F_GETFL, 0)) < 0)
127 : : {
128 : 0 : log_msg(LOG_ERR, "run_tcp_server: fcntl F_GETFL error: %s",
129 : 0 : strerror(errno));
130 : 0 : close(s_sock);
131 : 0 : return -1;
132 : : }
133 : :
134 : : #if !CODE_COVERAGE
135 : : sfd_flags |= O_NONBLOCK;
136 : :
137 : : if(fcntl(s_sock, F_SETFL, sfd_flags) < 0)
138 : : {
139 : : log_msg(LOG_ERR, "run_tcp_server: fcntl F_SETFL error setting O_NONBLOCK: %s",
140 : : strerror(errno));
141 : : close(s_sock);
142 : : return -1;
143 : : }
144 : : #endif
145 : :
146 : : /* Construct local address structure */
147 : : memset(&saddr, 0, sizeof(saddr));
148 : 1 : saddr.sin_family = AF_INET; /* Internet address family */
149 : 1 : saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
150 [ - + ]: 1 : saddr.sin_port = htons(port); /* Local port */
151 : :
152 : : /* Bind to the local address */
153 [ - + ]: 1 : if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
154 : : {
155 : 0 : log_msg(LOG_ERR, "run_tcp_server: bind() failed: %s",
156 : 0 : strerror(errno));
157 : 0 : close(s_sock);
158 : 0 : return -1;
159 : : }
160 : :
161 : : /* Mark the socket so it will listen for incoming connections
162 : : * (but only one at a time)
163 : : */
164 [ - + ]: 1 : if (listen(s_sock, 1) < 0)
165 : : {
166 : 0 : log_msg(LOG_ERR, "run_tcp_server: listen() failed: %s",
167 : 0 : strerror(errno));
168 : 0 : close(s_sock);
169 : 0 : return -1;
170 : : }
171 : :
172 : 1 : FD_ZERO(&sfd_set);
173 : :
174 : : /* Now loop and accept and drop connections after the first packet or a
175 : : * short timeout.
176 : : */
177 : : while(1)
178 : : {
179 : 6 : clen = sizeof(caddr);
180 : :
181 : : /* Initialize and setup the socket for select.
182 : : */
183 [ - + ][ # # ]: 6 : FD_SET(s_sock, &sfd_set);
184 : :
185 : : /* Set our select timeout to 200 ms.
186 : : */
187 : 6 : tv.tv_sec = 0;
188 : 6 : tv.tv_usec = 200000;
189 : :
190 : 6 : selval = select(s_sock+1, &sfd_set, NULL, NULL, &tv);
191 : :
192 [ - + ]: 6 : if(selval == -1)
193 : : {
194 : : /* Select error - so kill the child and bail.
195 : : */
196 : 0 : log_msg(LOG_ERR, "run_tcp_server: select error socket: %s",
197 : 0 : strerror(errno));
198 : 0 : rv = -1;
199 : 0 : break;
200 : : }
201 : :
202 : : #if !CODE_COVERAGE
203 : : if(selval == 0)
204 : : {
205 : : /* Timeout - So we check to make sure our parent is still there by simply
206 : : * using kill(ppid, 0) and checking the return value.
207 : : */
208 : : if(kill(ppid, 0) != 0 && errno == ESRCH)
209 : : {
210 : : rv = -1;
211 : : break;
212 : : }
213 : : continue;
214 : : }
215 : : #endif
216 : :
217 [ - + ][ # # ]: 6 : if(! FD_ISSET(s_sock, &sfd_set))
[ + + ]
218 : 5 : continue;
219 : :
220 : : /* Wait for a client to connect
221 : : */
222 [ - + ]: 1 : if((c_sock = accept(s_sock, (struct sockaddr *) &caddr, (socklen_t *)&clen)) < 0)
223 : : {
224 : 0 : log_msg(LOG_ERR, "run_tcp_server: accept() failed: %s",
225 : 0 : strerror(errno));
226 : 0 : rv = -1;
227 : 0 : break;
228 : : }
229 : :
230 [ + - ]: 1 : if(opts->verbose)
231 : : {
232 : : memset(sipbuf, 0x0, MAX_IPV4_STR_LEN);
233 : 1 : inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN);
234 : 1 : log_msg(LOG_INFO, "tcp_server: Got TCP connection from %s.", sipbuf);
235 : : }
236 : :
237 : : /* Though hacky and clunky, we just sleep for a second then
238 : : * close the socket. No need to read or write anything. This
239 : : * just gives the client a sufficient window to send their
240 : : * request on this socket. In any case the socket is closed
241 : : * after that time.
242 : : */
243 : 1 : usleep(1000000);
244 : 1 : shutdown(c_sock, SHUT_RDWR);
245 : 1 : close(c_sock);
246 : :
247 : : #if CODE_COVERAGE
248 : 1 : break;
249 : : #endif
250 : 5 : } /* infinite while loop */
251 : :
252 : 1 : close(s_sock);
253 : 1 : return rv;
254 : : }
255 : :
256 : : /***EOF***/
|