/*****************************************************************************\
* Copyright (c) 2002 Pelle Johansson. *
* All rights reserved. *
* *
* This file is part of the moftpd package. Use and distribution of *
* this software is governed by the terms in the file LICENCE, which *
* should have come with this package. *
\*****************************************************************************/
/* $moftpd: com_port.c 1251 2005-03-06 22:24:29Z morth $ */
#include "system.h"
#include "commands.h"
#include "connection.h"
#include "main.h"
#include "events.h"
int command_port(connection_t *conn, const char *arg, int expected)
{
int a1, a2, a3, a4, p1, p2;
unsigned int port;
struct sockaddr_in *addr4, addr;
if(conn->epsvOnly)
{
reply(conn, "503 You've chosen to disable the PORT command, use EPSV.");
return 0;
}
switch(sscanf(arg, "%d,%d,%d,%d,%d,%d", &a1, &a2, &a3, &a4, &p1, &p2))
{
case 6:
if(a1 < 0 || a1 > 255 || a2 < 0 || a2 > 255 || a3 < 0 || a3 > 255 ||
a4 < 0 || a4 > 255 || p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255)
{
reply(conn, "501 Invalid port specification.");
return 0;
}
addr.sin_addr.s_addr = htonl(a1 << 24 | a2 << 16 | a3 << 8 | a4);
port = p1 << 8 | p2;
if(!conn->server->allowLowPorts && port < 1024)
{
syslog (LOG_NOTICE, "%d: PORT with low port: %d", conn->id, port);
reply(conn, "501 Port < 1024 not allowed.");
return 0;
}
port = htons(port);
addr4 = (struct sockaddr_in*)&conn->rDataAddr;
if(!conn->user->allowForeign)
{
if(addr4->sin_family != AF_INET || addr4->sin_addr.s_addr != addr.sin_addr.s_addr)
{
syslog (LOG_NOTICE, "%d: PORT with foreign address", conn->id);
reply(conn, "501 Foreign address not allowed.");
return 0;
}
}
else if (!conn->user->allowOutOfRange && conn->server->numRanges)
{
addr.sin_family = AF_INET;
for (a1 = 0; a1 < conn->server->numRanges; a1++)
{
if (check_range ((struct sockaddr*)&addr,
(struct sockaddr*)&conn->server->ranges[a1].addr,
(struct sockaddr*)&conn->server->ranges[a1].mask))
break;
}
if (a1 == conn->server->numRanges)
{
syslog (LOG_NOTICE, "%d: PORT out of range", conn->id);
reply (conn, "501 Address out of allowed range.");
}
}
addr4->sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
addr4->sin_len = sizeof(struct sockaddr_in);
#endif
addr4->sin_port = port;
addr4->sin_addr.s_addr = addr.sin_addr.s_addr;
conn->passive = 0;
reply(conn, "200 Port set successfully.");
break;
default:
reply(conn, "501 Invalid port specification.");
break;
}
return 0;
}
int command_lprt (connection_t *conn, const char *arg, int expected)
{
int a[16], p[2];
unsigned char ca[16], cp[2];
int i, prt, al, pl;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
char buf[1024];
if (conn->epsvOnly)
{
reply (conn, "503 You've chosen to disable the LPRT command, use EPSV.");
return 0;
}
if (sscanf (arg, "%d,%d,%s", &prt, &al, buf) == 3)
{
if (prt != 4 && prt != 6)
{
reply (conn, "521 Supported address families are (4,6)");
return 0;
}
if ((prt == 4 && al != 4) || (prt == 6 && al != 16))
{
reply (conn, "500 Invalid address length.");
return 0;
}
for (i = 0; i < al; i++)
{
if (sscanf (buf, "%d,%s", a + i, buf) != 2)
{
reply (conn, "501 Number of fields doesn't match length given.");
return 0;
}
if (a[i] < 0 || a[i] > 255)
{
reply (conn, "500 Invalid long port specification.");
return 0;
}
ca[i] = a[i];
}
if (sscanf (buf, "%d,%s", &pl, buf) == 2)
{
if (pl != 2)
{
reply (conn, "500 Invalid port length.");
return 0;
}
if (sscanf (buf, "%d,%d", &p[0], &p[1]) != 2)
{
reply (conn, "501 Number of fields doesn't match length given.");
return 0;
}
cp[0] = p[0];
cp[1] = p[1];
pl = ntohs (*(unsigned short*)cp);
if(!conn->server->allowLowPorts && pl < 1024)
{
syslog (LOG_NOTICE, "%d: LPRT with low port: %d", conn->id, prt);
reply(conn, "501 Port < 1024 not allowed.");
return 0;
}
switch (prt)
{
case 4:
addr4.sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
addr4.sin_len = sizeof (struct sockaddr_in);
#endif
addr4.sin_port = *(unsigned short*)cp;
addr4.sin_addr.s_addr = *(unsigned int*)ca;
if (!conn->user->allowForeign)
{
if (!same_addr ((struct sockaddr*)&conn->rDataAddr, (struct sockaddr*)&addr4, 0))
{
syslog (LOG_NOTICE, "%d: LPRT with foreign address", conn->id);
reply(conn, "501 Foreign address not allowed.");
return 0;
}
}
else if (!conn->user->allowOutOfRange && conn->server->numRanges)
{
for (i = 0; i < conn->server->numRanges; i++)
{
if (check_range ((struct sockaddr*)&addr4,
(struct sockaddr*)&conn->server->ranges[i].addr,
(struct sockaddr*)&conn->server->ranges[i].mask))
break;
}
if (i == conn->server->numRanges)
{
syslog (LOG_NOTICE, "%d: LPRT out of range", conn->id);
reply (conn, "501 Address out of allowed range.");
}
}
memcpy (&conn->rDataAddr, &addr4, sizeof (struct sockaddr_in));
reply (conn, "200 Port set successfully.");
return 0;
case 6:
addr6.sin6_family = AF_INET6;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
addr6.sin6_len = sizeof (struct sockaddr_in6);
#endif
addr6.sin6_port = *(unsigned short*)cp;
memcpy (&addr6.sin6_addr, ca, 16);
if (!conn->user->allowForeign)
{
if (!same_addr ((struct sockaddr*)&conn->rDataAddr, (struct sockaddr*)&addr6, 0))
{
syslog (LOG_NOTICE, "%d: LPRT with foreign address", conn->id);
reply(conn, "501 Foreign address not allowed.");
return 0;
}
}
else if (!conn->user->allowOutOfRange && conn->server->numRanges)
{
for (i = 0; i < conn->server->numRanges; i++)
{
if (check_range ((struct sockaddr*)&addr6,
(struct sockaddr*)&conn->server->ranges[i].addr,
(struct sockaddr*)&conn->server->ranges[i].mask))
break;
}
if (i == conn->server->numRanges)
{
syslog (LOG_NOTICE, "%d: LPRT out of range", conn->id);
reply (conn, "501 Address out of allowed range.");
}
}
memcpy (&conn->rDataAddr, &addr6, sizeof (struct sockaddr_in6));
reply (conn, "200 Port set successfully.");
return 0;
}
}
}
reply (conn, "500 Invalid long port specification.");
return 0;
}
int command_eprt(connection_t *conn, const char *arg, int expected)
{
unsigned char d = *arg++;
char *narg;
int prt, i;
const char *addr, *port;
struct addrinfo hints = {0}, *res;
if(conn->epsvOnly)
{
reply(conn, "503 You've chosen to disable the EPRT command, use EPSV.");
return 0;
}
if(d < 33 || d > 126)
{
if (d)
syslog (LOG_NOTICE, "%d: EPRT with invalid delimeter: %d", conn->id, d);
reply(conn, "501 Delimeter missing or invalid.");
return 0;
}
narg = strchr(arg, d);
if(!narg)
{
reply(conn, "501 Protocol field missing.");
return 0;
}
*narg++ = 0;
prt = atoi(arg);
if(prt < 1 || prt > 2)
{
syslog (LOG_NOTICE, "%d: EPRT with invalid protocol: %d", conn->id, prt);
reply(conn, "522 Invalid protocol, use (1,2)");
return 0;
}
arg = narg;
narg = strchr(arg, d);
if(!narg)
{
reply(conn, "501 Address field missing.");
return 0;
}
*narg++ = 0;
addr = arg;
arg = narg;
narg = strchr(arg, d);
if(!narg)
{
reply(conn, "501 Port field missing.");
return 0;
}
*narg++ = 0;
port = arg;
hints.ai_flags = AI_NUMERICHOST;
hints.ai_socktype = SOCK_STREAM;
switch(prt)
{
case 1:
hints.ai_family = AF_INET;
break;
case 2:
hints.ai_family = AF_INET6;
break;
}
if(getaddrinfo(addr, port, &hints, &res) || !res)
{
reply(conn, "501 Invalid address specification.");
return 0;
}
switch(res->ai_family)
{
case AF_INET:
prt = ntohs(((struct sockaddr_in*)res->ai_addr)->sin_port);
break;
case AF_INET6:
prt = ntohs(((struct sockaddr_in6*)res->ai_addr)->sin6_port);
break;
}
if(!conn->user->allowForeign)
{
if (!same_addr((struct sockaddr*)&conn->rDataAddr, res->ai_addr, 0))
{
syslog (LOG_NOTICE, "%d: EPRT with foreign address", conn->id);
reply(conn, "501 Foreign address not allowed.");
return 0;
}
}
else if (!conn->user->allowOutOfRange && conn->server->numRanges)
{
for (i = 0; i < conn->server->numRanges; i++)
{
if (check_range (res->ai_addr,
(struct sockaddr*)&conn->server->ranges[i].addr,
(struct sockaddr*)&conn->server->ranges[i].mask))
break;
}
if (i == conn->server->numRanges)
{
syslog (LOG_NOTICE, "%d: EPRT out of range", conn->id);
reply (conn, "501 Address out of allowed range.");
}
}
if(!conn->server->allowLowPorts && prt < 1024)
{
syslog (LOG_NOTICE, "%d: EPRT with low port: %d", conn->id, prt);
reply(conn, "501 Port < 1024 not allowed.");
return 0;
}
memcpy(&conn->rDataAddr, res->ai_addr, res->ai_addrlen);
conn->passive = 0;
freeaddrinfo(res);
reply(conn, "200 Port set successfully.");
return 0;
}
int command_pasv(connection_t *conn, const char *arg, int expected)
{
int hport;
unsigned char *ap;
if(conn->epsvOnly)
{
reply(conn, "503 You've chosen to disable the PASV command, use EPSV.");
return 0;
}
if(conn->lDataAddr.ss_family != AF_INET)
{
reply(conn, "500 Not an Internet version 4 connection, use EPSV.");
return 0;
}
conn->passive = 0;
conn->passiveAccepted = 0;
if(conn->dataSock >= 0)
{
/*
* We know there's no transfer in progress, so safe to assume we can
* silently close this.
*/
close(conn->dataSock);
remove_read_fd(conn->dataSock);
remove_write_fd(conn->dataSock);
conn->dataSock = -1;
}
conn->dataSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(conn->dataSock < 0)
{
syslog (LOG_ERR, "%d: Failed to create passive socket: %m", conn->id);
reply(conn, "425 Failed to create socket: %s.", strerror(errno));
return 0;
}
for(hport = conn->server->minPasvPort; hport <= conn->server->maxPasvPort;
hport++)
{
((struct sockaddr_in*)&conn->lDataAddr)->sin_port = htons(hport);
if(!bind(conn->dataSock, (struct sockaddr*)&conn->lDataAddr,
sizeof(struct sockaddr_in)))
break;
}
if(hport > conn->server->maxPasvPort)
{
syslog (LOG_ERR, "%d: Failed to bind passive socket: %m", conn->id);
reply(conn, "425 Failed to bind socket: %s.", strerror(errno));
close(conn->dataSock);
conn->dataSock = -1;
return 0;
}
if(listen(conn->dataSock, 1))
{
syslog (LOG_ERR, "%d: Failed to listen on passive socket: %m", conn->id);
reply(conn, "425 Failed to listen on socket: %s.", strerror(errno));
close(conn->dataSock);
conn->dataSock = -1;
return 0;
}
add_read_fd(conn->dataSock, passive_acceptor, conn);
conn->passive = 1;
ap = (unsigned char*)&((struct sockaddr_in*)&conn->lDataAddr)->sin_addr;
reply(conn, "227 Entering passive mode (%u,%u,%u,%u,%u,%u)", ap[0], ap[1],
ap[2], ap[3], hport >> 8 & 0xFF, hport & 0xFF);
return 0;
}
int command_lpsv (connection_t *conn, const char *arg, int expected)
{
in_port_t *port;
int hport, al;
unsigned char *ca, *cp;
if(conn->epsvOnly)
{
reply(conn, "503 You've chosen to disable the LPSV command, use EPSV.");
return 0;
}
if (conn->dataSock >= 0)
{
/*
* We know there's no transfer in progress, so safe to assume we can
* silently close this.
*/
close (conn->dataSock);
remove_read_fd (conn->dataSock);
remove_write_fd (conn->dataSock);
conn->dataSock = -1;
}
conn->passive = 0;
conn->passiveAccepted = 0;
switch (conn->lDataAddr.ss_family)
{
case AF_INET:
port = &((struct sockaddr_in*)&conn->lDataAddr)->sin_port;
al = sizeof (struct sockaddr_in);
break;
case AF_INET6:
port = &((struct sockaddr_in6*)&conn->lDataAddr)->sin6_port;
al = sizeof (struct sockaddr_in6);
break;
default:
reply(conn, "500 Unknown address family.");
return 0;
}
conn->dataSock = socket (conn->lDataAddr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (conn->dataSock < 0)
{
syslog (LOG_ERR, "%d: Failed to create passive socket: %m", conn->id);
reply (conn, "425 Failed to create socket: %s.", strerror (errno));
return 0;
}
for (hport = conn->server->minPasvPort; hport <= conn->server->maxPasvPort;
hport++)
{
*port = htons (hport);
if(!bind (conn->dataSock, (struct sockaddr*)&conn->lDataAddr, al))
break;
}
if (hport > conn->server->maxPasvPort)
{
syslog (LOG_ERR, "%d: Failed to bind passive socket: %m", conn->id);
reply (conn, "425 Failed to bind socket: %s.", strerror (errno));
close (conn->dataSock);
conn->dataSock = -1;
return 0;
}
if (listen (conn->dataSock, 1))
{
syslog (LOG_ERR, "%d: Failed to listen on passive socket: %m", conn->id);
reply (conn, "425 Failed to listen on socket: %s.", strerror(errno));
close (conn->dataSock);
conn->dataSock = -1;
return 0;
}
conn->passive = 1;
add_read_fd (conn->dataSock, passive_acceptor, conn);
cp = (unsigned char*)port;
switch (conn->lDataAddr.ss_family)
{
case AF_INET:
ca = (unsigned char*)&((struct sockaddr_in*)&conn->lDataAddr)->sin_addr;
reply (conn, "228 Entering Long Passive Mode (4,4,%d,%d,%d,%d,2,%d,%d)",
ca[0], ca[1], ca[2], ca[3], cp[0], cp[1]);
break;
case AF_INET6:
ca = (unsigned char*)&((struct sockaddr_in6*)&conn->lDataAddr)->sin6_addr;
// Breaking a reply format makes it broken in the locale/messages file.
reply (conn, "228 Entering Long Passive Mode (6,16,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,2,%d,%d)",
ca[0], ca[1], ca[2], ca[3],
ca[4], ca[5], ca[6], ca[7], ca[8], ca[9], ca[10], ca[11], ca[12],
ca[13], ca[14], ca[15], cp[0], cp[1]);
break;
}
return 0;
}
int command_epsv(connection_t *conn, const char *arg, int expected)
{
in_port_t *port;
int hport, al;
if(!strcasecmp(arg, "ALL"))
{
if(conn->epsvOnly)
reply(conn, "200 Already in EPSV ALL mode.");
else
{
conn->epsvOnly = 1;
reply(conn, "200 EPSV ALL mode entered.");
}
return 0;
}
else if (strlen (arg))
{
if (!strcmp (arg, "1"))
{
if (conn->lDataAddr.ss_family != AF_INET)
{
reply (conn, "522 Must use same protocol as control connection.");
return 0;
}
}
else if (!strcmp (arg, "2"))
{
if (conn->lDataAddr.ss_family != AF_INET6)
{
reply (conn, "522 Must use same protocol as control connection.");
return 0;
}
}
else
{
reply (conn, "522 Unknown protocol number.");
return 0;
}
}
if(conn->dataSock >= 0)
{
/*
* We know there's no transfer in progress, so safe to assume we can
* silently close this.
*/
close(conn->dataSock);
remove_read_fd(conn->dataSock);
remove_write_fd(conn->dataSock);
conn->dataSock = -1;
}
conn->passive = 0;
conn->passiveAccepted = 0;
switch(conn->lDataAddr.ss_family)
{
case AF_INET:
port = &((struct sockaddr_in*)&conn->lDataAddr)->sin_port;
al = sizeof(struct sockaddr_in);
break;
case AF_INET6:
port = &((struct sockaddr_in6*)&conn->lDataAddr)->sin6_port;
al = sizeof(struct sockaddr_in6);
break;
default:
reply(conn, "500 Unknown address family.");
return 0;
}
conn->dataSock = socket(conn->lDataAddr.ss_family, SOCK_STREAM, IPPROTO_TCP);
if(conn->dataSock < 0)
{
syslog (LOG_ERR, "%d: Failed to create passive socket: %m", conn->id);
reply(conn, "425 Failed to create socket: %s.", strerror(errno));
return 0;
}
for(hport = conn->server->minPasvPort; hport <= conn->server->maxPasvPort;
hport++)
{
*port = htons(hport);
if(!bind(conn->dataSock, (struct sockaddr*)&conn->lDataAddr, al))
break;
}
if(hport > conn->server->maxPasvPort)
{
syslog (LOG_ERR, "%d: Failed to bind passive socket: %m", conn->id);
reply(conn, "425 Failed to bind socket: %s.", strerror(errno));
close(conn->dataSock);
conn->dataSock = -1;
return 0;
}
if(listen(conn->dataSock, 1))
{
syslog (LOG_ERR, "%d: Failed to listen on passive socket: %m", conn->id);
reply(conn, "425 Failed to listen on socket: %s.", strerror(errno));
close(conn->dataSock);
conn->dataSock = -1;
return 0;
}
conn->passive = 1;
add_read_fd(conn->dataSock, passive_acceptor, conn);
reply(conn, "229 Entering extended passive mode (|||%d|)", hport);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1