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