(connection_acceptor): Don't increase the
[oftpd.git] / src / ftp_listener.c
1 /*
2
3 $Id$
4  
5 This is the code that waits for client connections and creates the
6 threads that handle them.  When ftp_listener_init() is called, it binds
7 to the appropriate socket and sets up the other values for the
8 structure.  Then, when ftp_listener_start() is called, a thread is
9 started dedicatd to accepting connections.
10
11 This thread waits for input on two file descriptors.  If input arrives
12 on the socket, then it accepts the connection and creates a thread to
13 handle it.  If input arrives on the shutdown_request pipe, then the
14 thread terminates.  This is how ftp_listener_stop() signals the listener
15 to end.
16
17 */
18
19 #include <config.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <pthread.h>
28 #include <stdlib.h>
29 #include <limits.h>
30 #include <syslog.h>
31 #include <stdio.h>
32 #include <netdb.h>
33 #include <netinet/tcp.h>
34 #include <fcntl.h>
35
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
38 # include <time.h>
39 #else
40 # if HAVE_SYS_TIME_H
41 #  include <sys/time.h>
42 # else
43 #  include <time.h>
44 # endif
45 #endif
46
47 #include "daemon_assert.h"
48 #include "telnet_session.h"
49 #include "ftp_session.h"
50 #include "ftp_listener.h"
51 #include "watchdog.h"
52 #include "af_portability.h"
53
54
55
56 /* maximum number of consecutive errors in accept()
57    before we terminate listener                     */
58 #define MAX_ACCEPT_ERROR 10
59
60 /* buffer to hold address string */
61 #define ADDR_BUF_LEN 100
62
63 /* information for a specific connection */
64 typedef struct connection_info {
65     ftp_listener_t *ftp_listener;
66     telnet_session_t telnet_session;
67     ftp_session_t ftp_session;
68     watched_t watched;
69     
70     struct connection_info *next;
71 } connection_info_t;
72
73 /* prototypes */
74 static int invariant(const ftp_listener_t *f);
75 static void *connection_acceptor(ftp_listener_t *f);
76 static void addr_to_string(const sockaddr_storage_t *s, char *addr);
77 static void *connection_handler(connection_info_t *info);
78 static void connection_handler_cleanup(connection_info_t *info);
79
80 /* initialize an FTP listener */
81 int ftp_listener_init(ftp_listener_t *f, 
82                       char *address, 
83                       int port, 
84                       int max_connections,
85                       int inactivity_timeout,
86                       error_t *err)
87 {
88     sockaddr_storage_t sock_addr;
89     int fd;
90     int flags;
91     int pipefds[2];
92     int reuseaddr;
93     char dir[PATH_MAX+1];
94     char buf[ADDR_BUF_LEN+1];
95     const char *inet_ntop_ret;
96
97     daemon_assert(f != NULL);
98     daemon_assert(port >= 0);
99     daemon_assert(port < 65536);
100     daemon_assert(max_connections > 0);
101     daemon_assert(err != NULL);
102
103     /* get our current directory */
104     if (getcwd(dir, sizeof(dir)) == NULL) {
105         error_init(err, errno, "error getting current directory; %s",
106                    strerror(errno));
107         return 0;
108     }
109
110     /* set up our socket address */
111     memset(&sock_addr, 0, sizeof(sockaddr_storage_t));
112
113     if (address == NULL) {
114 #ifdef INET6
115         SAFAM(&sock_addr) = AF_INET6;
116         memcpy(&SIN6ADDR(&sock_addr), &in6addr_any, sizeof(struct in6_addr));
117 #else
118         SAFAM(&sock_addr) = AF_INET;
119         sock_addr.sin_addr.s_addr = INADDR_ANY;
120 #endif
121     } else {
122         int gai_err;
123         struct addrinfo hints;
124         struct addrinfo *res;
125
126         memset(&hints, 0, sizeof(hints));
127
128 /* - This code should be able to parse both IPv4 and IPv6 internet
129  * addresses and put them in the sock_addr variable.
130  * - Much neater now.
131  * - Bug: Can't handle hostnames, only IP addresses.  Not sure
132  * exactly why.  But then again, I'm not sure exactly why
133  * there is a man page for getipnodebyname (which getaddrinfo
134  * says it uses) but no corresponding function in libc.
135  * -- Matthew Danish [3/20/2001]
136  */
137
138 #ifdef INET6
139         hints.ai_family = AF_INET6;
140 #else
141         hints.ai_family = AF_INET;
142 #endif
143
144         hints.ai_flags  = AI_PASSIVE;
145
146         gai_err = getaddrinfo(address, NULL, &hints, &res);
147         if (gai_err != 0) {
148             error_init(err, gai_err, "error parsing server socket address; %s", 
149                        gai_strerror(gai_err));
150             return 0;
151         }
152
153         memcpy(&sock_addr, res->ai_addr, res->ai_addrlen);
154         freeaddrinfo(res);
155     } 
156
157     if (port == 0) {
158         SINPORT(&sock_addr) = htons(DEFAULT_FTP_PORT);
159     } else {
160         SINPORT(&sock_addr) = htons(port);
161     }
162
163
164     inet_ntop_ret = inet_ntop(SAFAM(&sock_addr), 
165                               (void *)&SINADDR(&sock_addr), 
166                               buf, 
167                               sizeof(buf));
168     if (inet_ntop_ret == NULL) {
169         error_init(err, errno, "error converting server address to ASCII; %s", 
170                    strerror(errno));
171         return 0;
172     }
173     
174     /* Put some information in syslog */
175     syslog(LOG_INFO, "Binding interface '%s', port %d, max clients %d\n", buf, 
176         ntohs(SINPORT(&sock_addr)), max_connections);    
177     
178
179     /* okay, finally do some socket manipulation */
180     fd = socket(AF_INET, SOCK_STREAM, 0);
181     if (fd == -1) {
182         error_init(err, errno, "error creating socket; %s", strerror(errno));
183         return 0;
184     }
185
186     reuseaddr = 1;
187     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuseaddr, 
188                    sizeof(int)) !=0) 
189     {
190         close(fd);
191         error_init(err, errno, "error setting socket to reuse address; %s", 
192                    strerror(errno));
193         return 0;
194     }
195
196     if (bind(fd, (struct sockaddr *)&sock_addr, 
197              sizeof(struct sockaddr_in)) != 0) 
198     {
199         close(fd);
200         error_init(err, errno, "error binding address; %s", strerror(errno));
201         return 0;
202     }
203
204     if (listen(fd, SOMAXCONN) != 0) {
205         close(fd);
206         error_init(err, errno, "error setting socket to listen; %s", 
207                    strerror(errno));
208         return 0;
209     }
210
211     /* prevent socket from blocking on accept() */
212     flags = fcntl(fd, F_GETFL);
213     if (flags == -1) {
214         close(fd);
215         error_init(err, errno, "error getting flags on socket; %s", 
216                    strerror(errno));
217         return 0;
218     }
219     if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
220         close(fd);
221         error_init(err, errno, "error setting socket to non-blocking; %s", 
222                    strerror(errno));
223         return 0;
224     }
225
226     /* create a pipe to wake up our listening thread */
227     if (pipe(pipefds) != 0) {
228         close(fd);
229         error_init(err, errno, "error creating pipe for internal use; %s", 
230                    strerror(errno));
231         return 0;
232     }
233
234     /* now load the values into the structure, since we can't fail from
235        here */
236     f->fd = fd;
237     f->max_connections = max_connections;
238     f->num_connections = 0;
239     f->inactivity_timeout = inactivity_timeout;
240     pthread_mutex_init(&f->mutex, NULL);
241
242     daemon_assert(strlen(dir) < sizeof(f->dir));
243     strcpy(f->dir, dir);
244     f->listener_running = 0;
245
246     f->shutdown_request_send_fd = pipefds[1];
247     f->shutdown_request_recv_fd = pipefds[0];
248     pthread_cond_init(&f->shutdown_cond, NULL);
249
250     daemon_assert(invariant(f));
251     return 1;
252 }
253
254 /* receive connections */
255 int ftp_listener_start(ftp_listener_t *f, error_t *err)
256 {
257     pthread_t thread_id;
258     int ret_val;
259     int error_code;
260
261     daemon_assert(invariant(f));
262     daemon_assert(err != NULL);
263
264     error_code = pthread_create(&thread_id, 
265                                 NULL, 
266                                 (void *(*)())connection_acceptor, 
267                                 f);
268
269     if (error_code == 0) {
270         f->listener_running = 1;
271         f->listener_thread = thread_id;
272         ret_val = 1;
273     } else {
274         error_init(err, error_code, "unable to create thread");
275         ret_val = 0;
276     }
277
278     daemon_assert(invariant(f));
279
280     return ret_val;
281 }
282
283
284 #ifndef NDEBUG
285 static int invariant(const ftp_listener_t *f) 
286 {
287     int dir_len;
288
289     if (f == NULL) {
290         return 0;
291     }
292     if (f->fd < 0) {
293         return 0;
294     }
295     if (f->max_connections <= 0) {
296         return 0;
297     }
298     if ((f->num_connections < 0) || (f->num_connections > f->max_connections)) {
299         return 0;
300     }
301     dir_len = strlen(f->dir);
302     if ((dir_len <= 0) || (dir_len > PATH_MAX)) {
303         return 0;
304     }
305     if (f->shutdown_request_send_fd < 0) {
306         return 0;
307     }
308     if (f->shutdown_request_recv_fd < 0) {
309         return 0;
310     }
311     return 1;
312 }
313 #endif /* NDEBUG */
314
315 /* handle incoming connections */
316 static void *connection_acceptor(ftp_listener_t *f)
317 {
318     error_t err;
319     int num_error;
320
321     int fd;
322     int tcp_nodelay;
323     sockaddr_storage_t client_addr;
324     sockaddr_storage_t server_addr;
325     unsigned addr_len;
326     
327     connection_info_t *info;
328     pthread_t thread_id;
329     int error_code;
330
331     fd_set readfds;
332
333     daemon_assert(invariant(f));
334
335     if (!watchdog_init(&f->watchdog, f->inactivity_timeout, &err)) {
336         syslog(LOG_ERR, "Error initializing watchdog thread; %s", 
337             error_get_desc(&err));
338         return NULL;
339     }
340
341     num_error = 0;
342     for (;;) {
343
344          /* wait for something to happen */
345          FD_ZERO(&readfds);
346          FD_SET(f->fd, &readfds);
347          FD_SET(f->shutdown_request_recv_fd, &readfds);
348          select(FD_SETSIZE, &readfds, NULL, NULL, NULL);
349
350          /* if data arrived on our pipe, we've been asked to exit */
351          if (FD_ISSET(f->shutdown_request_recv_fd, &readfds)) {
352              close(f->fd);
353              syslog(LOG_INFO, "listener no longer accepting connections");
354              pthread_exit(NULL);
355          }
356
357          /* otherwise accept our pending connection (if any) */
358          addr_len = sizeof(sockaddr_storage_t);
359          fd = accept(f->fd, (struct sockaddr *)&client_addr, &addr_len);
360          if (fd >= 0) {
361
362              tcp_nodelay = 1;
363              if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&tcp_nodelay,
364                  sizeof(int)) != 0)
365              {
366                  syslog(LOG_ERR,
367                    "error in setsockopt(), FTP server dropping connection; %s",
368                    strerror(errno));
369                  close(fd);
370                  continue;
371              }
372
373              addr_len = sizeof(sockaddr_storage_t);
374              if (getsockname(fd, (struct sockaddr *)&server_addr, 
375                  &addr_len) == -1) 
376              {
377                  syslog(LOG_ERR, 
378                    "error in getsockname(), FTP server dropping connection; %s",
379                    strerror(errno));
380                  close(fd);
381                  continue;
382              }
383
384              info = (connection_info_t *)malloc(sizeof(connection_info_t));
385              if (info == NULL) {
386                  syslog(LOG_CRIT, 
387                      "out of memory, FTP server dropping connection");
388                  close(fd);
389                  continue;
390              }
391              info->ftp_listener = f;
392
393              telnet_session_init(&info->telnet_session, fd, fd);
394
395              if (!ftp_session_init(&info->ftp_session, 
396                                    &client_addr, 
397                                    &server_addr, 
398                                    &info->telnet_session,  
399                                    f->dir, 
400                                    &err)) 
401              {
402                  syslog(LOG_ERR, 
403                      "error initializing FTP session, FTP server exiting; %s",
404                      error_get_desc(&err));
405                  close(fd);
406                  telnet_session_destroy(&info->telnet_session);
407                  free(info);
408                  continue;
409              }
410
411
412              error_code = pthread_create(&thread_id, 
413                           NULL, 
414                           (void *(*)())connection_handler, 
415                           info);
416
417              if (error_code != 0) {
418                  syslog(LOG_ERR, "error creating new thread; %d", error_code);
419                  close(fd);
420                  telnet_session_destroy(&info->telnet_session);
421                  free(info);
422              }
423
424              num_error = 0;
425          } else {
426              if ((errno == ECONNABORTED) || (errno == ECONNRESET)) {
427                  syslog(LOG_NOTICE, 
428                      "interruption accepting FTP connection; %s", 
429                      strerror(errno));
430              } else {
431                  syslog(LOG_WARNING, 
432                      "error accepting FTP connection; %s", 
433                      strerror(errno));
434                  /* We don't bump the error counter if we are merely
435                     out of file descriptors.  The hope is that some
436                     other thread will eventually finish and release
437                     its resources.  We have this problem ever few week
438                     on the ftp.gnupg.org site. */
439                  if (errno == EMFILE) {
440                      /* Wait a bit. */
441                      struct timeval tv;
442
443                      tv.tv_sec = 10;
444                      tv.tv_usec = 0;
445                      select(0, NULL, NULL, NULL, &tv);
446                  }
447                  else
448                      ++num_error;
449              }
450              if (num_error >= MAX_ACCEPT_ERROR) {
451                  syslog(LOG_ERR, 
452                    "too many consecutive errors, FTP server exiting");
453                  return NULL;
454              }
455          }
456     }
457 }
458
459 /* convert an address to a printable string */
460 /* NOT THREADSAFE - wrap with a mutex before calling! */
461 static char *addr2string(const sockaddr_storage_t *s)
462 {
463     static char addr[IP_ADDRSTRLEN+1];
464     int error;
465     char *ret_val;
466
467     daemon_assert(s != NULL);
468
469 #ifdef INET6
470     error = getnameinfo((struct sockaddr *)s, 
471                          sizeof(sockaddr_storage_t),
472                          addr, 
473                          sizeof(addr), 
474                          NULL,
475                          0, 
476                          NI_NUMERICHOST);
477     if (error != 0) {
478         syslog(LOG_WARN, "getnameinfo error; %s", gai_strerror(error));
479         ret_val = "Unknown IP";
480     } else {
481         ret_val = addr;
482     }
483 #else
484     ret_val = inet_ntoa(s->sin_addr);
485 #endif
486
487     return ret_val;
488 }
489
490
491 static void *connection_handler(connection_info_t *info) 
492 {
493     ftp_listener_t *f;
494     int num_connections;
495     char drop_reason[80];
496
497     /* for ease of use only */
498     f = info->ftp_listener;
499
500     /* don't save state for pthread_join() */
501     pthread_detach(pthread_self());
502
503     /* set up our watchdog */
504     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
505     watchdog_add_watched(&f->watchdog, &info->watched);
506
507     /* set up our cleanup handler */
508     pthread_cleanup_push((void (*)())connection_handler_cleanup, info);
509
510     /* process global data */
511     pthread_mutex_lock(&f->mutex);
512     num_connections = ++f->num_connections;
513     syslog(LOG_INFO, "%s port %d connection", 
514         addr2string(&info->ftp_session.client_addr), 
515         ntohs(SINPORT(&info->ftp_session.client_addr)));
516     pthread_mutex_unlock(&f->mutex);
517
518     /* handle the session */
519     if (num_connections <= f->max_connections) {
520
521         ftp_session_run(&info->ftp_session, &info->watched);
522
523     } else {
524
525         /* too many users */
526         sprintf(drop_reason, 
527           "Too many users logged in, dropping connection (%d logins maximum)", 
528           f->max_connections);
529         ftp_session_drop(&info->ftp_session, drop_reason);
530
531         /* log the rejection */
532         pthread_mutex_lock(&f->mutex);
533         syslog(LOG_WARNING, 
534           "%s port %d exceeds max users (%d), dropping connection",
535           addr2string(&info->ftp_session.client_addr), 
536           ntohs(SINPORT(&info->ftp_session.client_addr)),
537           num_connections);
538         pthread_mutex_unlock(&f->mutex);
539
540     }
541
542     /* exunt (pop calls cleanup function) */
543     pthread_cleanup_pop(1);
544
545     /* return for form :) */
546     return NULL;
547 }
548
549 /* clean up a connection */
550 static void connection_handler_cleanup(connection_info_t *info) 
551 {
552     ftp_listener_t *f;
553
554     f = info->ftp_listener;
555
556     watchdog_remove_watched(&info->watched);
557
558     pthread_mutex_lock(&f->mutex);
559
560     f->num_connections--;
561     pthread_cond_signal(&f->shutdown_cond);
562
563     syslog(LOG_INFO, 
564       "%s port %d disconnected", 
565       addr2string(&info->ftp_session.client_addr),
566       ntohs(SINPORT(&info->ftp_session.client_addr)));
567
568     pthread_mutex_unlock(&f->mutex);
569
570     ftp_session_destroy(&info->ftp_session);
571     telnet_session_destroy(&info->telnet_session);
572
573     free(info);
574 }
575
576 void ftp_listener_stop(ftp_listener_t *f)
577 {
578     daemon_assert(invariant(f));
579
580     /* write a byte to the listening thread - this will wake it up */
581     write(f->shutdown_request_send_fd, "", 1);
582
583     /* wait for client connections to complete */
584     pthread_mutex_lock(&f->mutex);
585     while (f->num_connections > 0) {
586         pthread_cond_wait(&f->shutdown_cond, &f->mutex);
587     }
588     pthread_mutex_unlock(&f->mutex);
589 }
590