/
niceSOFT
/
inetutils
Обзор
Документация
Войти
/
niceSOFT
/
inetutils
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
ftpd/server_mode.c
243 строки
6 KB
Simon Josefsson
maint: Run 'make update-copyright'
20 янв 2026, 11:50
Не верифицирован
20 янв 2026, 11:50
1ce61cd
Код
Авторство
О чём код?
/* Copyright (C) 2000-2026 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see `http://www.gnu.org/licenses/'. */ #include <config.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <string.h> #include <syslog.h> #include <stdio.h> #include <sys/stat.h> #include <unistd.h> #include <sys/wait.h> #include <errno.h> #ifdef HAVE_TCPD_H # include <tcpd.h> #endif #include <libinetutils.h> #include "attribute.h" int usefamily = AF_UNSPEC; /* Address family for daemon. */ static void reapchild (int); #ifndef DEFPORT # ifdef IPPORT_FTP # define DEFPORT IPPORT_FTP # else/* !IPPORT_FTP */ # define DEFPORT 21 # endif #endif /* !DEFPORT */ #ifdef WITH_WRAP # if !HAVE_DECL_HOSTS_CTL extern int hosts_ctl (char *, char *, char *, char *); # endif int allow_severity = LOG_INFO; int deny_severity = LOG_NOTICE; static int check_host (struct sockaddr *sa, socklen_t len) { int err; char addr[INET6_ADDRSTRLEN]; char name[NI_MAXHOST]; if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) return 1; (void) getnameinfo (sa, len, addr, sizeof (addr), NULL, 0, NI_NUMERICHOST); err = getnameinfo (sa, len, name, sizeof (name), NULL, 0, NI_NAMEREQD); if (!err) { if (!hosts_ctl ("ftpd", name, addr, STRING_UNKNOWN)) { syslog (deny_severity, "tcpwrappers rejected: %s [%s]", name, addr); return 0; } } else { if (!hosts_ctl ("ftpd", STRING_UNKNOWN, addr, STRING_UNKNOWN)) { syslog (deny_severity, "tcpwrappers rejected: [%s]", addr); return 0; } } return (1); } #endif /* WITH_WRAP */ static void reapchild (int signo MAYBE_UNUSED) { int save_errno = errno; while (waitpid (-1, NULL, WNOHANG) > 0) ; errno = save_errno; } /* The parameter '*phis_addrlen' must be initiated with the space available at calling time. The size of used space will then be returned. */ int server_mode (const char *pidfile, struct sockaddr *phis_addr, socklen_t *phis_addrlen, char *argv[]) { int ctl_sock, fd; struct servent *sv; int port, err; char portstr[8]; socklen_t saved_addrlen = *phis_addrlen; struct addrinfo hints, *res, *ai; /* Become a daemon. */ if (daemon (1, 1) < 0) { syslog (LOG_ERR, "failed to become a daemon"); return -1; } signal (SIGCHLD, reapchild); /* Get port for ftp/tcp. */ sv = getservbyname ("ftp", "tcp"); port = (sv == NULL) ? DEFPORT : ntohs (sv->s_port); snprintf (portstr, sizeof (portstr), "%u", port); memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; /* If an undetermined address family is passed, * we build a dual stacked listener from an AF_INET6 * socket by unsetting the sockopt IPV6_V6ONLY. * Otherwise the resolver often prefers AF_INET. */ hints.ai_family = (usefamily != AF_UNSPEC) ? usefamily : AF_INET6; err = getaddrinfo (NULL, portstr, &hints, &res); if (err) { syslog (LOG_ERR, "control socket: %s", gai_strerror (err)); return -1; } /* Attempt to open socket, bind and start listen. */ for (ai = res; ai; ai = ai->ai_next) { ctl_sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (ctl_sock < 0) continue; /* Enable local address reuse. */ { int on = 1; if (setsockopt (ctl_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) syslog (LOG_ERR, "control setsockopt: %m"); } /* Upgrade to dual stacked socket. */ if (usefamily == AF_UNSPEC && ai->ai_family == AF_INET6) { int off = 0; if (setsockopt (ctl_sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &off, sizeof (off)) < 0) syslog (LOG_DEBUG, "setsockopt bindv6only: %m"); } if (bind (ctl_sock, ai->ai_addr, ai->ai_addrlen)) { close (ctl_sock); ctl_sock = -1; continue; } if (listen (ctl_sock, 32) < 0) { close (ctl_sock); ctl_sock = -1; continue; } /* Accept the first choice! */ break; } if (res) freeaddrinfo (res); if (ai == NULL) { syslog (LOG_ERR, "control socket: %m"); return -1; } /* Stash pid in pidfile. */ { FILE *pid_fp = fopen (pidfile, "w"); if (pid_fp == NULL) syslog (LOG_ERR, "can't open %s: %m", PATH_FTPDPID); else { fprintf (pid_fp, "%d\n", (int) getpid ()); fchmod (fileno (pid_fp), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fclose (pid_fp); } } /* Loop forever accepting connection requests and forking off children to handle them. */ while (1) { *phis_addrlen = saved_addrlen; fd = accept (ctl_sock, phis_addr, phis_addrlen); if (fork () == 0) /* child */ { dup2 (fd, 0); dup2 (fd, 1); close (ctl_sock); break; } close (fd); } #ifdef WITH_WRAP /* In the child. */ if (!check_host (phis_addr, *phis_addrlen)) return -1; #endif #ifndef HAVE_FORK _exit (execvp (argv[0], argv)); #else (void) argv; /* Silence warnings. */ #endif return fd; }