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