#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include "extern.h" #include "pcplisten.h" #define PCPLISTEN "/bin/pcplisten" int pcplisten_available(void) { return 0 == access(PCPLISTEN, X_OK); } int pcplisten_start(/*INOUT*/struct sockaddr_storage *addr, int timeout_sec, /*OUT*/char **perrstr) { FILE *fp; char buf[256]; char ipstr[256]; unsigned port; int status = 0; int result = -2; #ifdef FTPD_DEBUG int errn = 0; #endif /* FTPD_DEBUG */ sighandler_t old_handler; *perrstr = 0; snprintf(buf, sizeof(buf), "%s tcp %s %u %u ftp-data", PCPLISTEN, sockaddr_addr2string(addr), sockaddr_port(addr), timeout_sec); buf[sizeof(buf)-1] = '\0'; // SIGCHLD must not being set to SIG_IGN to make pclose() to work old_handler = signal(SIGCHLD, SIG_DFL); fp = popen(buf, "r"); if (!fp) { signal(SIGCHLD, old_handler); return -1; } Log(("%s getting output", __FUNCTION__)); if (buf == fgets(buf, sizeof(buf), fp)) { buf[sizeof(buf)-1] = '\0'; Log(("%s got '%s'", __FUNCTION__, buf)); if (2 == sscanf(buf, "OK: %255s %u", ipstr, &port) && port > 0 && port <= 65535) { int family = AF_INET; if (strchr(ipstr, ':')) family = AF_INET6; struct sockaddr_storage *ss = numericaladdr2sockaddr_storage(family, ipstr); if (ss) { switch(ss->ss_family) { case AF_INET: ((struct sockaddr_in *)ss)->sin_port = htons(port); break; #ifdef USE_IPV6 case AF_INET6: ((struct sockaddr_in6 *)ss)->sin6_port = htons(port); break; #endif } memcpy(addr, ss, sizeof(*addr)); free(ss); result = 0; } } } else buf[0] = '\0'; Log(("%s pclose", __FUNCTION__)); status = pclose(fp); #ifdef FTPD_DEBUG if (-1 == status) errn = errno; Log(("%s pclose status=%d errn=%d", __FUNCTION__, status, errn)); #endif /* FTPD_DEBUG */ signal(SIGCHLD, old_handler); if (-1 != status && WIFEXITED(status)) { status = WEXITSTATUS(status); Log(("%s pclose exit code=%d", __FUNCTION__, status)); switch(status) { case 0: /* listen port opened */ /* ok - dont change result */ break; case 1: /* wrong usage */ result = -1; break; case 2: /* pcp error */ if (0 < strlen(buf)) { char *p = &buf[strlen(buf)]; while(p > buf && (*(p-1) == '\n' || *(p-1) == '\r')) { p--; *p = '\0'; } if ('\0' != buf[0]) *perrstr = strdup(buf); } result = -1; break; case 3: /* no pcp server */ // this is considered as ok, we want to use the original addr result = 0; break; default: result = -1; } } return result; }