/*****************************************************************************\
* 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