Mercurial > dropbear
comparison main.c @ 4:fe6bca95afa7
Makefile.in contains updated files required
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 01 Jun 2004 02:46:09 +0000 |
parents | |
children | bc6477a6c393 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 4:fe6bca95afa7 |
---|---|
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, runopts * opts, 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 runopts * opts; | |
57 FILE * pidfile; | |
58 | |
59 int childsock; | |
60 pid_t childpid; | |
61 int childpipe[2]; | |
62 | |
63 struct sigaction sa_chld; | |
64 | |
65 /* get commandline options */ | |
66 opts = getrunopts(argc, argv); | |
67 | |
68 /* fork */ | |
69 if (opts->forkbg) { | |
70 int closefds = 0; | |
71 #ifndef DEBUG_TRACE | |
72 if (!usingsyslog) { | |
73 closefds = 1; | |
74 } | |
75 #endif | |
76 if (daemon(0, closefds) < 0) { | |
77 dropbear_exit("Failed to create background process: %s", | |
78 strerror(errno)); | |
79 } | |
80 } | |
81 | |
82 #ifndef DISABLE_SYSLOG | |
83 if (usingsyslog) { | |
84 startsyslog(); | |
85 } | |
86 #endif | |
87 | |
88 /* should be done after syslog is working */ | |
89 if (opts->forkbg) { | |
90 dropbear_log(LOG_INFO, "Running in background"); | |
91 } else { | |
92 dropbear_log(LOG_INFO, "Not forking"); | |
93 } | |
94 | |
95 /* create a PID file so that we can be killed easily */ | |
96 pidfile = fopen(DROPBEAR_PIDFILE, "w"); | |
97 if (pidfile) { | |
98 fprintf(pidfile, "%d\n", getpid()); | |
99 fclose(pidfile); | |
100 } | |
101 | |
102 /* set up cleanup handler */ | |
103 if (signal(SIGINT, sigintterm_handler) == SIG_ERR || | |
104 #ifndef DEBUG_VALGRIND | |
105 signal(SIGTERM, sigintterm_handler) == SIG_ERR || | |
106 #endif | |
107 signal(SIGPIPE, SIG_IGN) == SIG_ERR) { | |
108 dropbear_exit("signal() error"); | |
109 } | |
110 | |
111 /* catch and reap zombie children */ | |
112 sa_chld.sa_handler = sigchld_handler; | |
113 sa_chld.sa_flags = SA_NOCLDSTOP; | |
114 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { | |
115 dropbear_exit("signal() error"); | |
116 } | |
117 if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) { | |
118 dropbear_exit("signal() error"); | |
119 } | |
120 | |
121 /* sockets to identify pre-authenticated clients */ | |
122 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
123 childpipes[i] = -1; | |
124 } | |
125 | |
126 /* Set up the listening sockets */ | |
127 /* XXX XXX ports */ | |
128 listensockcount = listensockets(listensocks, opts, &maxsock); | |
129 | |
130 /* incoming connection select loop */ | |
131 for(;;) { | |
132 | |
133 FD_ZERO(&fds); | |
134 | |
135 seltimeout.tv_sec = 60; | |
136 seltimeout.tv_usec = 0; | |
137 | |
138 /* listening sockets */ | |
139 for (i = 0; i < listensockcount; i++) { | |
140 FD_SET(listensocks[i], &fds); | |
141 } | |
142 | |
143 /* pre-authentication clients */ | |
144 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
145 if (childpipes[i] >= 0) { | |
146 FD_SET(childpipes[i], &fds); | |
147 maxsock = MAX(maxsock, childpipes[i]); | |
148 } | |
149 } | |
150 | |
151 val = select(maxsock+1, &fds, NULL, NULL, &seltimeout); | |
152 | |
153 if (exitflag) { | |
154 unlink(DROPBEAR_PIDFILE); | |
155 dropbear_exit("Terminated by signal"); | |
156 } | |
157 | |
158 if (val == 0) { | |
159 /* timeout reached */ | |
160 continue; | |
161 } | |
162 | |
163 if (val < 0) { | |
164 if (errno == EINTR) { | |
165 continue; | |
166 } | |
167 dropbear_exit("Listening socket error"); | |
168 } | |
169 | |
170 /* close fds which have been authed or closed - auth.c handles | |
171 * closing the auth sockets on success */ | |
172 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
173 if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) { | |
174 close(childpipes[i]); | |
175 childpipes[i] = -1; | |
176 } | |
177 } | |
178 | |
179 /* handle each socket which has something to say */ | |
180 for (i = 0; i < listensockcount; i++) { | |
181 if (!FD_ISSET(listensocks[i], &fds)) | |
182 continue; | |
183 | |
184 /* child connection XXX - ip6 stuff here */ | |
185 remoteaddrlen = sizeof(struct sockaddr_in); | |
186 childsock = accept(listensocks[i], &remoteaddr, &remoteaddrlen); | |
187 | |
188 if (childsock < 0) { | |
189 /* accept failed */ | |
190 continue; | |
191 } | |
192 | |
193 /* check for max number of connections not authorised */ | |
194 for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) { | |
195 if (childpipes[j] < 0) { | |
196 break; | |
197 } | |
198 } | |
199 | |
200 if (j == MAX_UNAUTH_CLIENTS) { | |
201 /* no free connections */ | |
202 /* TODO - possibly log, though this would be an easy way | |
203 * to fill logs/disk */ | |
204 close(childsock); | |
205 continue; | |
206 } | |
207 | |
208 if (pipe(childpipe) < 0) { | |
209 TRACE(("error creating child pipe")); | |
210 close(childsock); | |
211 continue; | |
212 } | |
213 | |
214 if ((childpid = fork()) == 0) { | |
215 | |
216 /* child */ | |
217 char * addrstring = NULL; | |
218 #ifdef DEBUG_FORKGPROF | |
219 extern void _start(void), etext(void); | |
220 monstartup((u_long)&_start, (u_long)&etext); | |
221 #endif /* DEBUG_FORKGPROF */ | |
222 | |
223 addrstring = getaddrstring(&remoteaddr); | |
224 dropbear_log(LOG_INFO, "Child connection from %s", addrstring); | |
225 m_free(addrstring); | |
226 | |
227 if (setsid() < 0) { | |
228 dropbear_exit("setsid: %s", strerror(errno)); | |
229 } | |
230 | |
231 /* make sure we close sockets */ | |
232 for (i = 0; i < listensockcount; i++) { | |
233 if (m_close(listensocks[i]) == DROPBEAR_FAILURE) { | |
234 dropbear_exit("Couldn't close socket"); | |
235 } | |
236 } | |
237 | |
238 if (m_close(childpipe[0]) == DROPBEAR_FAILURE) { | |
239 dropbear_exit("Couldn't close socket"); | |
240 } | |
241 /* start the session */ | |
242 child_session(childsock, opts, childpipe[1], &remoteaddr); | |
243 /* don't return */ | |
244 assert(0); | |
245 } | |
246 | |
247 /* parent */ | |
248 childpipes[j] = childpipe[0]; | |
249 if (m_close(childpipe[1]) == DROPBEAR_FAILURE | |
250 || m_close(childsock) == DROPBEAR_FAILURE) { | |
251 dropbear_exit("Couldn't close socket"); | |
252 } | |
253 } | |
254 } /* for(;;) loop */ | |
255 | |
256 /* don't reach here */ | |
257 return -1; | |
258 } | |
259 #endif | |
260 | |
261 /* catch + reap zombie children */ | |
262 static void sigchld_handler(int fish) { | |
263 struct sigaction sa_chld; | |
264 | |
265 while(waitpid(-1, NULL, WNOHANG) > 0); | |
266 | |
267 sa_chld.sa_handler = sigchld_handler; | |
268 sa_chld.sa_flags = SA_NOCLDSTOP; | |
269 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { | |
270 dropbear_exit("signal() error"); | |
271 } | |
272 } | |
273 | |
274 /* catch any segvs */ | |
275 static void sigsegv_handler(int fish) { | |
276 fprintf(stderr, "Aiee, segfault! You should probably report " | |
277 "this as a bug to the developer\n"); | |
278 exit(EXIT_FAILURE); | |
279 } | |
280 | |
281 /* catch ctrl-c or sigterm */ | |
282 static void sigintterm_handler(int fish) { | |
283 | |
284 exitflag = 1; | |
285 } | |
286 | |
287 /* Set up listening sockets for all the requested ports */ | |
288 static int listensockets(int *sock, runopts * opts, int *maxfd) { | |
289 | |
290 int listensock; /* listening fd */ | |
291 struct sockaddr_in listen_addr; | |
292 struct linger linger; | |
293 unsigned int i; | |
294 int val; | |
295 | |
296 for (i = 0; i < opts->portcount; i++) { | |
297 | |
298 /* iterate through all the sockets to listen on */ | |
299 listensock = socket(PF_INET, SOCK_STREAM, 0); | |
300 if (listensock < 0) { | |
301 dropbear_exit("Failed to create socket"); | |
302 } | |
303 | |
304 val = 1; | |
305 /* set to reuse, quick timeout */ | |
306 setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, | |
307 (void*) &val, sizeof(val)); | |
308 linger.l_onoff = 1; | |
309 linger.l_linger = 5; | |
310 setsockopt(listensock, SOL_SOCKET, SO_LINGER, | |
311 (void*)&linger, sizeof(linger)); | |
312 | |
313 /* disable nagle */ | |
314 setsockopt(listensock, IPPROTO_TCP, TCP_NODELAY, | |
315 (void*)&val, sizeof(val)); | |
316 | |
317 memset((void*)&listen_addr, 0x0, sizeof(listen_addr)); | |
318 listen_addr.sin_family = AF_INET; | |
319 listen_addr.sin_port = htons(opts->ports[i]); | |
320 listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
321 memset(&(listen_addr.sin_zero), '\0', 8); | |
322 | |
323 if (bind(listensock, (struct sockaddr *)&listen_addr, | |
324 sizeof(listen_addr)) < 0) { | |
325 dropbear_exit("Bind failed port %d", opts->ports[i]); | |
326 } | |
327 | |
328 /* listen */ | |
329 if (listen(listensock, 20) < 0) { /* TODO set listen count */ | |
330 dropbear_exit("Listen failed"); | |
331 } | |
332 | |
333 /* nonblock */ | |
334 if (fcntl(listensock, F_SETFL, O_NONBLOCK) < 0) { | |
335 dropbear_exit("Failed to set non-blocking"); | |
336 } | |
337 | |
338 sock[i] = listensock; | |
339 *maxfd = MAX(listensock, *maxfd); | |
340 } | |
341 | |
342 return opts->portcount; | |
343 } |