Blame src/network.cc

Packit Service a2489d
/*
Packit Service a2489d
 * lftp - file transfer program
Packit Service a2489d
 *
Packit Service a2489d
 * Copyright (c) 1996-2015 by Alexander V. Lukyanov (lav@yars.free.net)
Packit Service a2489d
 *
Packit Service a2489d
 * This program is free software; you can redistribute it and/or modify
Packit Service a2489d
 * it under the terms of the GNU General Public License as published by
Packit Service a2489d
 * the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
 * (at your option) any later version.
Packit Service a2489d
 *
Packit Service a2489d
 * This program is distributed in the hope that it will be useful,
Packit Service a2489d
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
 * GNU General Public License for more details.
Packit Service a2489d
 *
Packit Service a2489d
 * You should have received a copy of the GNU General Public License
Packit Service a2489d
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service a2489d
 */
Packit Service a2489d
Packit Service a2489d
#include <config.h>
Packit Service a2489d
#include <stdio.h>
Packit Service a2489d
#include <sys/types.h>
Packit Service a2489d
#include <netdb.h>
Packit Service a2489d
#include <errno.h>
Packit Service a2489d
#include <unistd.h>
Packit Service a2489d
#include <fcntl.h>
Packit Service a2489d
#include <netinet/in.h>
Packit Service a2489d
#ifdef HAVE_NETINET_IN_SYSTM_H
Packit Service a2489d
# include <netinet/in_systm.h>
Packit Service a2489d
#endif
Packit Service a2489d
#ifdef HAVE_NETINET_IP_H
Packit Service a2489d
# include <netinet/ip.h>
Packit Service a2489d
#endif
Packit Service a2489d
#ifdef HAVE_NETINET_TCP_H
Packit Service a2489d
# include <netinet/tcp.h>
Packit Service a2489d
#endif
Packit Service a2489d
#ifdef HAVE_SYS_IOCTL_H
Packit Service a2489d
# include <sys/ioctl.h>
Packit Service a2489d
#endif
Packit Service a2489d
#ifdef HAVE_TERMIOS_H
Packit Service a2489d
# include <termios.h>
Packit Service a2489d
#endif
Packit Service a2489d
#include "SMTask.h"
Packit Service a2489d
#include "network.h"
Packit Service a2489d
#include "ResMgr.h"
Packit Service a2489d
#include "ProtoLog.h"
Packit Service a2489d
#include "xstring.h"
Packit Service a2489d
Packit Service a2489d
const char *sockaddr_u::address() const
Packit Service a2489d
{
Packit Service a2489d
#ifdef HAVE_GETNAMEINFO
Packit Service a2489d
   char *buf=xstring::tmp_buf(NI_MAXHOST);
Packit Service a2489d
   if(getnameinfo(&sa,addr_len(),buf,NI_MAXHOST,0,0,NI_NUMERICHOST)<0)
Packit Service a2489d
      return "????";
Packit Service a2489d
   return buf;
Packit Service a2489d
#else
Packit Service a2489d
   static char buf[16];
Packit Service a2489d
   if(sa.sa_family!=AF_INET)
Packit Service a2489d
      return "????";
Packit Service a2489d
   unsigned char *a=(unsigned char *)&in.sin_addr;
Packit Service a2489d
   snprintf(buf,16,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
Packit Service a2489d
   return buf;
Packit Service a2489d
#endif
Packit Service a2489d
}
Packit Service a2489d
int sockaddr_u::port() const
Packit Service a2489d
{
Packit Service a2489d
   if(sa.sa_family==AF_INET)
Packit Service a2489d
      return ntohs(in.sin_port);
Packit Service a2489d
#if INET6
Packit Service a2489d
   if(sa.sa_family==AF_INET6)
Packit Service a2489d
      return ntohs(in6.sin6_port);
Packit Service a2489d
#endif
Packit Service a2489d
   return 0;
Packit Service a2489d
}
Packit Service a2489d
void sockaddr_u::set_port(int port)
Packit Service a2489d
{
Packit Service a2489d
   if(sa.sa_family==AF_INET)
Packit Service a2489d
      in.sin_port=htons(port);
Packit Service a2489d
#if INET6
Packit Service a2489d
   if(sa.sa_family==AF_INET6)
Packit Service a2489d
      in6.sin6_port=htons(port);
Packit Service a2489d
#endif
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const xstring& sockaddr_u::to_xstring() const
Packit Service a2489d
{
Packit Service a2489d
   return xstring::format("[%s]:%d",address(),port());
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool sockaddr_u::is_reserved() const
Packit Service a2489d
{
Packit Service a2489d
   if(sa.sa_family==AF_INET)
Packit Service a2489d
   {
Packit Service a2489d
      unsigned char *a=(unsigned char *)&in.sin_addr;
Packit Service a2489d
      return (a[0]==0)
Packit Service a2489d
	  || (a[0]==127 && !is_loopback())
Packit Service a2489d
	  || (a[0]>=240);
Packit Service a2489d
   }
Packit Service a2489d
#if INET6
Packit Service a2489d
   if(family()==AF_INET6) {
Packit Service a2489d
      return IN6_IS_ADDR_UNSPECIFIED(&in6.sin6_addr)
Packit Service a2489d
	  || IN6_IS_ADDR_V4MAPPED(&in6.sin6_addr)
Packit Service a2489d
	  || IN6_IS_ADDR_V4COMPAT(&in6.sin6_addr);
Packit Service a2489d
   }
Packit Service a2489d
#endif
Packit Service a2489d
   return false;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool sockaddr_u::is_multicast() const
Packit Service a2489d
{
Packit Service a2489d
   if(sa.sa_family==AF_INET)
Packit Service a2489d
   {
Packit Service a2489d
      unsigned char *a=(unsigned char *)&in.sin_addr;
Packit Service a2489d
      return (a[0]>=224 && a[0]<240);
Packit Service a2489d
   }
Packit Service a2489d
#if INET6
Packit Service a2489d
   if(family()==AF_INET6)
Packit Service a2489d
      return IN6_IS_ADDR_MULTICAST(&in6.sin6_addr);
Packit Service a2489d
#endif
Packit Service a2489d
   return false;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool sockaddr_u::is_private() const
Packit Service a2489d
{
Packit Service a2489d
   if(sa.sa_family==AF_INET)
Packit Service a2489d
   {
Packit Service a2489d
      unsigned char *a=(unsigned char *)&in.sin_addr;
Packit Service a2489d
      return (a[0]==10)
Packit Service a2489d
	  || (a[0]==172 && a[1]>=16 && a[1]<32)
Packit Service a2489d
	  || (a[0]==192 && a[1]==168)
Packit Service a2489d
	  || (a[0]==169 && a[1]==254); // self-assigned
Packit Service a2489d
   }
Packit Service a2489d
#if INET6
Packit Service a2489d
   if(family()==AF_INET6) {
Packit Service a2489d
      return IN6_IS_ADDR_SITELOCAL(&in6.sin6_addr)
Packit Service a2489d
	  || IN6_IS_ADDR_LINKLOCAL(&in6.sin6_addr);
Packit Service a2489d
   }
Packit Service a2489d
#endif
Packit Service a2489d
   return false;
Packit Service a2489d
}
Packit Service a2489d
bool sockaddr_u::is_loopback() const
Packit Service a2489d
{
Packit Service a2489d
   if(sa.sa_family==AF_INET)
Packit Service a2489d
   {
Packit Service a2489d
      unsigned char *a=(unsigned char *)&in.sin_addr;
Packit Service a2489d
      return (a[0]==127 && a[1]==0 && a[2]==0 && a[3]==1);
Packit Service a2489d
   }
Packit Service a2489d
#if INET6
Packit Service a2489d
   if(sa.sa_family==AF_INET6)
Packit Service a2489d
      return IN6_IS_ADDR_LOOPBACK(&in6.sin6_addr);
Packit Service a2489d
#endif
Packit Service a2489d
   return false;
Packit Service a2489d
}
Packit Service a2489d
bool sockaddr_u::is_compatible(const sockaddr_u& o) const
Packit Service a2489d
{
Packit Service a2489d
   return family()==o.family()
Packit Service a2489d
      && !is_multicast() && !o.is_multicast()
Packit Service a2489d
      && !is_reserved() && !o.is_reserved()
Packit Service a2489d
      && is_private()==o.is_private()
Packit Service a2489d
      && is_loopback()==o.is_loopback();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool sockaddr_u::set_compact(const char *c,size_t len)
Packit Service a2489d
{
Packit Service a2489d
   if(len==4) {
Packit Service a2489d
      sa.sa_family=AF_INET;
Packit Service a2489d
      memcpy(&in.sin_addr,c,4);
Packit Service a2489d
      in.sin_port=0;
Packit Service a2489d
      return true;
Packit Service a2489d
#if INET6
Packit Service a2489d
   } else if(len==16) {
Packit Service a2489d
      sa.sa_family=AF_INET6;
Packit Service a2489d
      memcpy(&in6.sin6_addr,c,16);
Packit Service a2489d
      return true;
Packit Service a2489d
#endif
Packit Service a2489d
   } else if(len==6) {
Packit Service a2489d
      sa.sa_family=AF_INET;
Packit Service a2489d
      memcpy(&in.sin_addr,c,4);
Packit Service a2489d
      in.sin_port=htons((c[5]&255)|((c[4]&255)<<8));
Packit Service a2489d
      return true;
Packit Service a2489d
#if INET6
Packit Service a2489d
   } else if(len==18) {
Packit Service a2489d
      sa.sa_family=AF_INET6;
Packit Service a2489d
      memcpy(&in6.sin6_addr,c,16);
Packit Service a2489d
      in6.sin6_port=htons((c[17]&255)|((c[16]&255)<<8));
Packit Service a2489d
      return true;
Packit Service a2489d
#endif
Packit Service a2489d
   }
Packit Service a2489d
   return false;
Packit Service a2489d
}
Packit Service a2489d
const sockaddr_compact& sockaddr_u::compact() const
Packit Service a2489d
{
Packit Service a2489d
   sockaddr_compact& c=compact_addr();
Packit Service a2489d
   int p=port();
Packit Service a2489d
   if(c.length() && p) {
Packit Service a2489d
      c.append(char(p>>8));
Packit Service a2489d
      c.append(char(p&255));
Packit Service a2489d
   }
Packit Service a2489d
   return c;
Packit Service a2489d
}
Packit Service a2489d
sockaddr_compact& sockaddr_u::compact_addr() const
Packit Service a2489d
{
Packit Service a2489d
   sockaddr_compact& c=sockaddr_compact::get_tmp();
Packit Service a2489d
   if(family()==AF_INET)
Packit Service a2489d
      c.append((const char*)&in.sin_addr,4);
Packit Service a2489d
#if INET6
Packit Service a2489d
   else if(family()==AF_INET6)
Packit Service a2489d
      c.append((const char*)&in6.sin6_addr,16);
Packit Service a2489d
#endif
Packit Service a2489d
   return c;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void Networker::NonBlock(int fd)
Packit Service a2489d
{
Packit Service a2489d
   int fl=fcntl(fd,F_GETFL);
Packit Service a2489d
   fcntl(fd,F_SETFL,fl|O_NONBLOCK);
Packit Service a2489d
}
Packit Service a2489d
void Networker::CloseOnExec(int fd)
Packit Service a2489d
{
Packit Service a2489d
   fcntl(fd,F_SETFD,FD_CLOEXEC);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
static int one=1;
Packit Service a2489d
void Networker::KeepAlive(int sock)
Packit Service a2489d
{
Packit Service a2489d
   setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,(char*)&one,sizeof(one));
Packit Service a2489d
}
Packit Service a2489d
void Networker::MinimizeLatency(int sock)
Packit Service a2489d
{
Packit Service a2489d
#ifdef IP_TOS
Packit Service a2489d
   int tos = IPTOS_LOWDELAY;
Packit Service a2489d
   setsockopt(sock, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int));
Packit Service a2489d
#endif
Packit Service a2489d
}
Packit Service a2489d
void Networker::MaximizeThroughput(int sock)
Packit Service a2489d
{
Packit Service a2489d
#ifdef IP_TOS
Packit Service a2489d
   int tos = IPTOS_THROUGHPUT;
Packit Service a2489d
   setsockopt(sock, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int));
Packit Service a2489d
#endif
Packit Service a2489d
}
Packit Service a2489d
void Networker::ReuseAddress(int sock)
Packit Service a2489d
{
Packit Service a2489d
   setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char*)&one,sizeof(one));
Packit Service a2489d
}
Packit Service a2489d
void Networker::SetSocketBuffer(int sock,int socket_buffer)
Packit Service a2489d
{
Packit Service a2489d
   if(socket_buffer==0)
Packit Service a2489d
      return;
Packit Service a2489d
   if(-1==setsockopt(sock,SOL_SOCKET,SO_SNDBUF,(char*)&socket_buffer,sizeof(socket_buffer)))
Packit Service a2489d
      ProtoLog::LogError(1,"setsockopt(SO_SNDBUF,%d): %s",socket_buffer,strerror(errno));
Packit Service a2489d
   if(-1==setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char*)&socket_buffer,sizeof(socket_buffer)))
Packit Service a2489d
      ProtoLog::LogError(1,"setsockopt(SO_RCVBUF,%d): %s",socket_buffer,strerror(errno));
Packit Service a2489d
}
Packit Service a2489d
void Networker::SetSocketMaxseg(int sock,int socket_maxseg)
Packit Service a2489d
{
Packit Service a2489d
#ifndef SOL_TCP
Packit Service a2489d
# define SOL_TCP IPPROTO_TCP
Packit Service a2489d
#endif
Packit Service a2489d
#ifdef TCP_MAXSEG
Packit Service a2489d
   if(socket_maxseg==0)
Packit Service a2489d
      return;
Packit Service a2489d
   if(-1==setsockopt(sock,SOL_TCP,TCP_MAXSEG,(char*)&socket_maxseg,sizeof(socket_maxseg)))
Packit Service a2489d
      ProtoLog::LogError(1,"setsockopt(TCP_MAXSEG,%d): %s",socket_maxseg,strerror(errno));
Packit Service a2489d
#endif
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
int Networker::SocketCreateUnbound(int af,int type,int proto,const char *hostname)
Packit Service a2489d
{
Packit Service a2489d
   int s=socket(af,type,proto);
Packit Service a2489d
   if(s<0)
Packit Service a2489d
      return s;
Packit Service a2489d
Packit Service a2489d
   NonBlock(s);
Packit Service a2489d
   CloseOnExec(s);
Packit Service a2489d
   SetSocketBuffer(s,ResMgr::Query("net:socket-buffer",hostname));
Packit Service a2489d
   return s;
Packit Service a2489d
}
Packit Service a2489d
bool sockaddr_u::set_defaults(int af,const char *hostname,int port)
Packit Service a2489d
{
Packit Service a2489d
   memset(this,0,sizeof(*this));
Packit Service a2489d
   sa.sa_family=af;
Packit Service a2489d
   const char *b=0;
Packit Service a2489d
   if(af==AF_INET)
Packit Service a2489d
   {
Packit Service a2489d
      b=ResMgr::Query("net:socket-bind-ipv4",hostname);
Packit Service a2489d
      if(!(b && b[0] && inet_pton(af,b,&in.sin_addr)))
Packit Service a2489d
	 b=0;
Packit Service a2489d
      in.sin_port=htons(port);
Packit Service a2489d
   }
Packit Service a2489d
#if INET6
Packit Service a2489d
   else if(af==AF_INET6)
Packit Service a2489d
   {
Packit Service a2489d
      b=ResMgr::Query("net:socket-bind-ipv6",hostname);
Packit Service a2489d
      if(!(b && b[0] && inet_pton(af,b,&in6.sin6_addr)))
Packit Service a2489d
	 b=0;
Packit Service a2489d
      in6.sin6_port=htons(port);
Packit Service a2489d
   }
Packit Service a2489d
#endif
Packit Service a2489d
   return b || port;
Packit Service a2489d
}
Packit Service a2489d
void Networker::SocketBindStd(int s,int af,const char *hostname,int port)
Packit Service a2489d
{
Packit Service a2489d
   sockaddr_u bind_addr;
Packit Service a2489d
   if(bind_addr.set_defaults(af,hostname,port))
Packit Service a2489d
   {
Packit Service a2489d
      if(bind_addr.bind_to(s)==-1)
Packit Service a2489d
	 ProtoLog::LogError(0,"bind(%s): %s",bind_addr.to_string(),strerror(errno));
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
int Networker::SocketCreate(int af,int type,int proto,const char *hostname)
Packit Service a2489d
{
Packit Service a2489d
   int s=SocketCreateUnbound(af,type,proto,hostname);
Packit Service a2489d
   if(s<0)
Packit Service a2489d
      return s;
Packit Service a2489d
   SocketBindStd(s,af,hostname);
Packit Service a2489d
   return s;
Packit Service a2489d
}
Packit Service a2489d
void Networker::SocketTuneTCP(int s,const char *hostname)
Packit Service a2489d
{
Packit Service a2489d
   KeepAlive(s);
Packit Service a2489d
   SetSocketMaxseg(s,ResMgr::Query("net:socket-maxseg",hostname));
Packit Service a2489d
}
Packit Service a2489d
int Networker::SocketCreateTCP(int af,const char *hostname)
Packit Service a2489d
{
Packit Service a2489d
   int s=SocketCreate(af,SOCK_STREAM,IPPROTO_TCP,hostname);
Packit Service a2489d
   if(s<0)
Packit Service a2489d
      return s;
Packit Service a2489d
   SocketTuneTCP(s,hostname);
Packit Service a2489d
   return s;
Packit Service a2489d
}
Packit Service a2489d
int Networker::SocketCreateUnboundTCP(int af,const char *hostname)
Packit Service a2489d
{
Packit Service a2489d
   int s=SocketCreateUnbound(af,SOCK_STREAM,IPPROTO_TCP,hostname);
Packit Service a2489d
   if(s<0)
Packit Service a2489d
      return s;
Packit Service a2489d
   SocketTuneTCP(s,hostname);
Packit Service a2489d
   return s;
Packit Service a2489d
}
Packit Service a2489d
int Networker::SocketConnect(int fd,const sockaddr_u *u)
Packit Service a2489d
{
Packit Service a2489d
   // some systems have wrong connect() prototype, so we have to cast off const.
Packit Service a2489d
   // in any case, connect does not alter the address.
Packit Service a2489d
   int res=connect(fd,(sockaddr*)&u->sa,SocketAddrLen(u));
Packit Service a2489d
   if(res!=-1)
Packit Service a2489d
      SMTask::UpdateNow(); // if non-blocking doesn't work
Packit Service a2489d
   return res;
Packit Service a2489d
}
Packit Service a2489d
int Networker::SocketAccept(int fd,sockaddr_u *u,const char *hostname)
Packit Service a2489d
{
Packit Service a2489d
   socklen_t len=sizeof(*u);
Packit Service a2489d
   int a=accept(fd,&u->sa,&len;;
Packit Service a2489d
   if(a<0)
Packit Service a2489d
      return a;
Packit Service a2489d
   NonBlock(a);
Packit Service a2489d
   CloseOnExec(a);
Packit Service a2489d
   KeepAlive(a);
Packit Service a2489d
   SetSocketBuffer(a,ResMgr::Query("net:socket-buffer",hostname));
Packit Service a2489d
   SetSocketMaxseg(a,ResMgr::Query("net:socket-maxseg",hostname));
Packit Service a2489d
   return a;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void Networker::SocketSinglePF(int s,int pf)
Packit Service a2489d
{
Packit Service a2489d
#if INET6 && defined(IPV6_V6ONLY)
Packit Service a2489d
   if(pf==PF_INET6) {
Packit Service a2489d
      int on = 1;
Packit Service a2489d
      if(-1==setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)))
Packit Service a2489d
	 ProtoLog::LogError(1,"setsockopt(IPV6_V6ONLY): %s",strerror(errno));
Packit Service a2489d
   }
Packit Service a2489d
#endif
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#ifdef TIOCOUTQ
Packit Service a2489d
static bool TIOCOUTQ_returns_free_space;
Packit Service a2489d
static bool TIOCOUTQ_works;
Packit Service a2489d
static bool TIOCOUTQ_tested;
Packit Service a2489d
static void test_TIOCOUTQ()
Packit Service a2489d
{
Packit Service a2489d
   int sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
Packit Service a2489d
   if(sock==-1)
Packit Service a2489d
      return;
Packit Service a2489d
   TIOCOUTQ_tested=true;
Packit Service a2489d
   int avail=-1;
Packit Service a2489d
   socklen_t len=sizeof(avail);
Packit Service a2489d
   if(getsockopt(sock,SOL_SOCKET,SO_SNDBUF,(char*)&avail,&len)==-1)
Packit Service a2489d
      avail=-1;
Packit Service a2489d
   int buf=-1;
Packit Service a2489d
   if(ioctl(sock,TIOCOUTQ,&buf)==-1)
Packit Service a2489d
      buf=-1;
Packit Service a2489d
   if(buf>=0 && avail>0 && (buf==0 || buf==avail))
Packit Service a2489d
   {
Packit Service a2489d
      TIOCOUTQ_works=true;
Packit Service a2489d
      TIOCOUTQ_returns_free_space=(buf==avail);
Packit Service a2489d
   }
Packit Service a2489d
   close(sock);
Packit Service a2489d
}
Packit Service a2489d
#endif
Packit Service a2489d
int Networker::SocketBuffered(int sock)
Packit Service a2489d
{
Packit Service a2489d
#ifdef TIOCOUTQ
Packit Service a2489d
   if(!TIOCOUTQ_tested)
Packit Service a2489d
      test_TIOCOUTQ();
Packit Service a2489d
   if(!TIOCOUTQ_works)
Packit Service a2489d
      return 0;
Packit Service a2489d
   int buffer=0;
Packit Service a2489d
   if(TIOCOUTQ_returns_free_space)
Packit Service a2489d
   {
Packit Service a2489d
      socklen_t len=sizeof(buffer);
Packit Service a2489d
      if(getsockopt(sock,SOL_SOCKET,SO_SNDBUF,(char*)&buffer,&len)==-1)
Packit Service a2489d
	 return 0;
Packit Service a2489d
      int avail=buffer;
Packit Service a2489d
      if(ioctl(sock,TIOCOUTQ,&avail)==-1)
Packit Service a2489d
	 return 0;
Packit Service a2489d
      if(avail>buffer)
Packit Service a2489d
	 return 0; // something wrong
Packit Service a2489d
      buffer-=avail;
Packit Service a2489d
      buffer=buffer*3/4; // approx...
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
   {
Packit Service a2489d
      if(ioctl(sock,TIOCOUTQ,&buffer)==-1)
Packit Service a2489d
	 return 0;
Packit Service a2489d
   }
Packit Service a2489d
   return buffer;
Packit Service a2489d
#else
Packit Service a2489d
   return 0;
Packit Service a2489d
#endif
Packit Service a2489d
}