b07b0db348955dc78ec3e0be17e7e1da22eb1861
[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                      ++num_error;
441              }
442              if (num_error >= MAX_ACCEPT_ERROR) {
443                  syslog(LOG_ERR, 
444                    "too many consecutive errors, FTP server exiting");
445                  return NULL;
446              }
447          }
448     }
449 }
450
451 /* convert an address to a printable string */
452 /* NOT THREADSAFE - wrap with a mutex before calling! */
453 static char *addr2string(const sockaddr_storage_t *s)
454 {
455     static char addr[IP_ADDRSTRLEN+1];
456     int error;
457     char *ret_val;
458
459     daemon_assert(s != NULL);
460
461 #ifdef INET6
462     error = getnameinfo((struct sockaddr *)s, 
463                          sizeof(sockaddr_storage_t),
464                          addr, 
465                          sizeof(addr), 
466                          NULL,
467                          0, 
468                          NI_NUMERICHOST);
469     if (error != 0) {
470         syslog(LOG_WARN, "getnameinfo error; %s", gai_strerror(error));
471         ret_val = "Unknown IP";
472     } else {
473         ret_val = addr;
474     }
475 #else
476     ret_val = inet_ntoa(s->sin_addr);
477 #endif
478
479     return ret_val;
480 }
481
482
483 static void *connection_handler(connection_info_t *info) 
484 {
485     ftp_listener_t *f;
486     int num_connections;
487     char drop_reason[80];
488
489     /* for ease of use only */
490     f = info->ftp_listener;
491
492     /* don't save state for pthread_join() */
493     pthread_detach(pthread_self());
494
495     /* set up our watchdog */
496     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
497     watchdog_add_watched(&f->watchdog, &info->watched);
498
499     /* set up our cleanup handler */
500     pthread_cleanup_push((void (*)())connection_handler_cleanup, info);
501
502     /* process global data */
503     pthread_mutex_lock(&f->mutex);
504     num_connections = ++f->num_connections;
505     syslog(LOG_INFO, "%s port %d connection", 
506         addr2string(&info->ftp_session.client_addr), 
507         ntohs(SINPORT(&info->ftp_session.client_addr)));
508     pthread_mutex_unlock(&f->mutex);
509
510     /* handle the session */
511     if (num_connections <= f->max_connections) {
512
513         ftp_session_run(&info->ftp_session, &info->watched);
514
515     } else {
516
517         /* too many users */
518         sprintf(drop_reason, 
519           "Too many users logged in, dropping connection (%d logins maximum)", 
520           f->max_connections);
521         ftp_session_drop(&info->ftp_session, drop_reason);
522
523         /* log the rejection */
524         pthread_mutex_lock(&f->mutex);
525         syslog(LOG_WARNING, 
526           "%s port %d exceeds max users (%d), dropping connection",
527           addr2string(&info->ftp_session.client_addr), 
528           ntohs(SINPORT(&info->ftp_session.client_addr)),
529           num_connections);
530         pthread_mutex_unlock(&f->mutex);
531
532     }
533
534     /* exunt (pop calls cleanup function) */
535     pthread_cleanup_pop(1);
536
537     /* return for form :) */
538     return NULL;
539 }
540
541 /* clean up a connection */
542 static void connection_handler_cleanup(connection_info_t *info) 
543 {
544     ftp_listener_t *f;
545
546     f = info->ftp_listener;
547
548     watchdog_remove_watched(&info->watched);
549
550     pthread_mutex_lock(&f->mutex);
551
552     f->num_connections--;
553     pthread_cond_signal(&f->shutdown_cond);
554
555     syslog(LOG_INFO, 
556       "%s port %d disconnected", 
557       addr2string(&info->ftp_session.client_addr),
558       ntohs(SINPORT(&info->ftp_session.client_addr)));
559
560     pthread_mutex_unlock(&f->mutex);
561
562     ftp_session_destroy(&info->ftp_session);
563     telnet_session_destroy(&info->telnet_session);
564
565     free(info);
566 }
567
568 void ftp_listener_stop(ftp_listener_t *f)
569 {
570     daemon_assert(invariant(f));
571
572     /* write a byte to the listening thread - this will wake it up */
573     write(f->shutdown_request_send_fd, "", 1);
574
575     /* wait for client connections to complete */
576     pthread_mutex_lock(&f->mutex);
577     while (f->num_connections > 0) {
578         pthread_cond_wait(&f->shutdown_cond, &f->mutex);
579     }
580     pthread_mutex_unlock(&f->mutex);
581 }
582