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