Mercurial > dropbear
comparison svr-main.c @ 30:223b0f5f8dce
Switching to the magical new Makefile, and new dbmulti style
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 27 Jul 2004 14:44:43 +0000 |
parents | |
children | 20563735e8b5 |
comparison
equal
deleted
inserted
replaced
29:0fcf63e1cb01 | 30:223b0f5f8dce |
---|---|
1 /* | |
2 * Dropbear - a SSH2 server | |
3 * | |
4 * Copyright (c) 2002,2003 Matt Johnston | |
5 * All rights reserved. | |
6 * | |
7 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 * of this software and associated documentation files (the "Software"), to deal | |
9 * in the Software without restriction, including without limitation the rights | |
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 * copies of the Software, and to permit persons to whom the Software is | |
12 * furnished to do so, subject to the following conditions: | |
13 * | |
14 * The above copyright notice and this permission notice shall be included in | |
15 * all copies or substantial portions of the Software. | |
16 * | |
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
23 * SOFTWARE. */ | |
24 | |
25 #include "includes.h" | |
26 #include "dbutil.h" | |
27 #include "session.h" | |
28 #include "buffer.h" | |
29 #include "signkey.h" | |
30 #include "runopts.h" | |
31 | |
32 static int listensockets(int *sock, int *maxfd); | |
33 static void sigchld_handler(int dummy); | |
34 static void sigsegv_handler(int); | |
35 static void sigintterm_handler(int fish); | |
36 | |
37 static int childpipes[MAX_UNAUTH_CLIENTS]; | |
38 | |
39 #if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI) | |
40 #if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI) | |
41 int dropbear_main(int argc, char ** argv) | |
42 #else | |
43 int main(int argc, char ** argv) | |
44 #endif | |
45 { | |
46 | |
47 fd_set fds; | |
48 struct timeval seltimeout; | |
49 unsigned int i, j; | |
50 int val; | |
51 int maxsock = -1; | |
52 struct sockaddr remoteaddr; | |
53 int remoteaddrlen; | |
54 int listensocks[MAX_LISTEN_ADDR]; | |
55 unsigned int listensockcount = 0; | |
56 FILE * pidfile; | |
57 | |
58 int childsock; | |
59 pid_t childpid; | |
60 int childpipe[2]; | |
61 | |
62 struct sigaction sa_chld; | |
63 | |
64 _dropbear_exit = svr_dropbear_exit; | |
65 _dropbear_log = svr_dropbear_log; | |
66 | |
67 /* get commandline options */ | |
68 svr_getopts(argc, argv); | |
69 | |
70 /* fork */ | |
71 if (svr_opts.forkbg) { | |
72 int closefds = 0; | |
73 #ifndef DEBUG_TRACE | |
74 if (!svr_opts.usingsyslog) { | |
75 closefds = 1; | |
76 } | |
77 #endif | |
78 if (daemon(0, closefds) < 0) { | |
79 dropbear_exit("Failed to create background process: %s", | |
80 strerror(errno)); | |
81 } | |
82 } | |
83 | |
84 #ifndef DISABLE_SYSLOG | |
85 if (svr_opts.usingsyslog) { | |
86 startsyslog(); | |
87 } | |
88 #endif | |
89 | |
90 /* should be done after syslog is working */ | |
91 if (svr_opts.forkbg) { | |
92 dropbear_log(LOG_INFO, "Running in background"); | |
93 } else { | |
94 dropbear_log(LOG_INFO, "Not forking"); | |
95 } | |
96 | |
97 /* create a PID file so that we can be killed easily */ | |
98 pidfile = fopen(DROPBEAR_PIDFILE, "w"); | |
99 if (pidfile) { | |
100 fprintf(pidfile, "%d\n", getpid()); | |
101 fclose(pidfile); | |
102 } | |
103 | |
104 /* set up cleanup handler */ | |
105 if (signal(SIGINT, sigintterm_handler) == SIG_ERR || | |
106 #ifndef DEBUG_VALGRIND | |
107 signal(SIGTERM, sigintterm_handler) == SIG_ERR || | |
108 #endif | |
109 signal(SIGPIPE, SIG_IGN) == SIG_ERR) { | |
110 dropbear_exit("signal() error"); | |
111 } | |
112 | |
113 /* catch and reap zombie children */ | |
114 sa_chld.sa_handler = sigchld_handler; | |
115 sa_chld.sa_flags = SA_NOCLDSTOP; | |
116 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { | |
117 dropbear_exit("signal() error"); | |
118 } | |
119 if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) { | |
120 dropbear_exit("signal() error"); | |
121 } | |
122 | |
123 /* sockets to identify pre-authenticated clients */ | |
124 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
125 childpipes[i] = -1; | |
126 } | |
127 | |
128 /* Set up the listening sockets */ | |
129 /* XXX XXX ports */ | |
130 listensockcount = listensockets(listensocks, &maxsock); | |
131 | |
132 /* incoming connection select loop */ | |
133 for(;;) { | |
134 | |
135 FD_ZERO(&fds); | |
136 | |
137 seltimeout.tv_sec = 60; | |
138 seltimeout.tv_usec = 0; | |
139 | |
140 /* listening sockets */ | |
141 for (i = 0; i < listensockcount; i++) { | |
142 FD_SET(listensocks[i], &fds); | |
143 } | |
144 | |
145 /* pre-authentication clients */ | |
146 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
147 if (childpipes[i] >= 0) { | |
148 FD_SET(childpipes[i], &fds); | |
149 maxsock = MAX(maxsock, childpipes[i]); | |
150 } | |
151 } | |
152 | |
153 val = select(maxsock+1, &fds, NULL, NULL, &seltimeout); | |
154 | |
155 if (exitflag) { | |
156 unlink(DROPBEAR_PIDFILE); | |
157 dropbear_exit("Terminated by signal"); | |
158 } | |
159 | |
160 if (val == 0) { | |
161 /* timeout reached */ | |
162 continue; | |
163 } | |
164 | |
165 if (val < 0) { | |
166 if (errno == EINTR) { | |
167 continue; | |
168 } | |
169 dropbear_exit("Listening socket error"); | |
170 } | |
171 | |
172 /* close fds which have been authed or closed - auth.c handles | |
173 * closing the auth sockets on success */ | |
174 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
175 if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) { | |
176 close(childpipes[i]); | |
177 childpipes[i] = -1; | |
178 } | |
179 } | |
180 | |
181 /* handle each socket which has something to say */ | |
182 for (i = 0; i < listensockcount; i++) { | |
183 if (!FD_ISSET(listensocks[i], &fds)) | |
184 continue; | |
185 | |
186 /* child connection XXX - ip6 stuff here */ | |
187 remoteaddrlen = sizeof(struct sockaddr_in); | |
188 childsock = accept(listensocks[i], &remoteaddr, &remoteaddrlen); | |
189 | |
190 if (childsock < 0) { | |
191 /* accept failed */ | |
192 continue; | |
193 } | |
194 | |
195 /* check for max number of connections not authorised */ | |
196 for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) { | |
197 if (childpipes[j] < 0) { | |
198 break; | |
199 } | |
200 } | |
201 | |
202 if (j == MAX_UNAUTH_CLIENTS) { | |
203 /* no free connections */ | |
204 /* TODO - possibly log, though this would be an easy way | |
205 * to fill logs/disk */ | |
206 close(childsock); | |
207 continue; | |
208 } | |
209 | |
210 if (pipe(childpipe) < 0) { | |
211 TRACE(("error creating child pipe")); | |
212 close(childsock); | |
213 continue; | |
214 } | |
215 | |
216 if ((childpid = fork()) == 0) { | |
217 | |
218 /* child */ | |
219 char * addrstring = NULL; | |
220 #ifdef DEBUG_FORKGPROF | |
221 extern void _start(void), etext(void); | |
222 monstartup((u_long)&_start, (u_long)&etext); | |
223 #endif /* DEBUG_FORKGPROF */ | |
224 | |
225 addrstring = getaddrstring(&remoteaddr); | |
226 dropbear_log(LOG_INFO, "Child connection from %s", addrstring); | |
227 m_free(addrstring); | |
228 | |
229 if (setsid() < 0) { | |
230 dropbear_exit("setsid: %s", strerror(errno)); | |
231 } | |
232 | |
233 /* make sure we close sockets */ | |
234 for (i = 0; i < listensockcount; i++) { | |
235 if (m_close(listensocks[i]) == DROPBEAR_FAILURE) { | |
236 dropbear_exit("Couldn't close socket"); | |
237 } | |
238 } | |
239 | |
240 if (m_close(childpipe[0]) == DROPBEAR_FAILURE) { | |
241 dropbear_exit("Couldn't close socket"); | |
242 } | |
243 | |
244 /* start the session */ | |
245 svr_session(childsock, childpipe[1], | |
246 getaddrhostname(&remoteaddr)); | |
247 /* don't return */ | |
248 assert(0); | |
249 } | |
250 | |
251 /* parent */ | |
252 childpipes[j] = childpipe[0]; | |
253 if (m_close(childpipe[1]) == DROPBEAR_FAILURE | |
254 || m_close(childsock) == DROPBEAR_FAILURE) { | |
255 dropbear_exit("Couldn't close socket"); | |
256 } | |
257 } | |
258 } /* for(;;) loop */ | |
259 | |
260 /* don't reach here */ | |
261 return -1; | |
262 } | |
263 #endif | |
264 | |
265 /* catch + reap zombie children */ | |
266 static void sigchld_handler(int fish) { | |
267 struct sigaction sa_chld; | |
268 | |
269 while(waitpid(-1, NULL, WNOHANG) > 0); | |
270 | |
271 sa_chld.sa_handler = sigchld_handler; | |
272 sa_chld.sa_flags = SA_NOCLDSTOP; | |
273 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { | |
274 dropbear_exit("signal() error"); | |
275 } | |
276 } | |
277 | |
278 /* catch any segvs */ | |
279 static void sigsegv_handler(int fish) { | |
280 fprintf(stderr, "Aiee, segfault! You should probably report " | |
281 "this as a bug to the developer\n"); | |
282 exit(EXIT_FAILURE); | |
283 } | |
284 | |
285 /* catch ctrl-c or sigterm */ | |
286 static void sigintterm_handler(int fish) { | |
287 | |
288 exitflag = 1; | |
289 } | |
290 | |
291 /* Set up listening sockets for all the requested ports */ | |
292 static int listensockets(int *sock, int *maxfd) { | |
293 | |
294 int listensock; /* listening fd */ | |
295 struct sockaddr_in listen_addr; | |
296 struct linger linger; | |
297 unsigned int i; | |
298 int val; | |
299 | |
300 for (i = 0; i < svr_opts.portcount; i++) { | |
301 | |
302 /* iterate through all the sockets to listen on */ | |
303 listensock = socket(PF_INET, SOCK_STREAM, 0); | |
304 if (listensock < 0) { | |
305 dropbear_exit("Failed to create socket"); | |
306 } | |
307 | |
308 val = 1; | |
309 /* set to reuse, quick timeout */ | |
310 setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, | |
311 (void*) &val, sizeof(val)); | |
312 linger.l_onoff = 1; | |
313 linger.l_linger = 5; | |
314 setsockopt(listensock, SOL_SOCKET, SO_LINGER, | |
315 (void*)&linger, sizeof(linger)); | |
316 | |
317 /* disable nagle */ | |
318 setsockopt(listensock, IPPROTO_TCP, TCP_NODELAY, | |
319 (void*)&val, sizeof(val)); | |
320 | |
321 memset((void*)&listen_addr, 0x0, sizeof(listen_addr)); | |
322 listen_addr.sin_family = AF_INET; | |
323 listen_addr.sin_port = htons(svr_opts.ports[i]); | |
324 listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
325 memset(&(listen_addr.sin_zero), '\0', 8); | |
326 | |
327 if (bind(listensock, (struct sockaddr *)&listen_addr, | |
328 sizeof(listen_addr)) < 0) { | |
329 dropbear_exit("Bind failed port %d", svr_opts.ports[i]); | |
330 } | |
331 | |
332 /* listen */ | |
333 if (listen(listensock, 20) < 0) { /* TODO set listen count */ | |
334 dropbear_exit("Listen failed"); | |
335 } | |
336 | |
337 /* nonblock */ | |
338 if (fcntl(listensock, F_SETFL, O_NONBLOCK) < 0) { | |
339 dropbear_exit("Failed to set non-blocking"); | |
340 } | |
341 | |
342 sock[i] = listensock; | |
343 *maxfd = MAX(listensock, *maxfd); | |
344 } | |
345 | |
346 return svr_opts.portcount; | |
347 } |