You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
436 lines
9.4 KiB
436 lines
9.4 KiB
/*
|
|
* This file is part of the KDE libraries
|
|
* Copyright (C) 1997 Torben Weis (weis@kde.org)
|
|
*
|
|
* $Id$
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
// on Linux/libc5, this includes linux/socket.h where SOMAXCONN is defined
|
|
#include <sys/socket.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/time.h>
|
|
#include <sys/un.h>
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
extern "C" {
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
}
|
|
|
|
#define KSOCK_NO_BROKEN
|
|
#include "kdebug.h"
|
|
#include "ksock.h"
|
|
#include "kextsock.h"
|
|
#include "ksockaddr.h"
|
|
|
|
#include "ksocks.h"
|
|
|
|
extern "C" {
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAVE_GETADDRINFO
|
|
#include <netdb.h>
|
|
#endif
|
|
|
|
// defines MAXDNAME under Solaris
|
|
#include <arpa/nameser.h>
|
|
#include <resolv.h>
|
|
}
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
|
|
#ifdef HAVE_SYSENT_H
|
|
#include <sysent.h>
|
|
#endif
|
|
|
|
#if TIME_WITH_SYS_TIME
|
|
#include <time.h>
|
|
#endif
|
|
|
|
|
|
// Play it safe, use a reasonable default, if SOMAXCONN was nowhere defined.
|
|
#ifndef SOMAXCONN
|
|
#warning Your header files do not seem to support SOMAXCONN
|
|
#define SOMAXCONN 5
|
|
#endif
|
|
|
|
#include <qapplication.h>
|
|
#include <qsocketnotifier.h>
|
|
|
|
#include "netsupp.h" // leave this last
|
|
|
|
#ifdef __CYGWIN__
|
|
#include "qwindowdefs.h"
|
|
#endif
|
|
|
|
class KSocketPrivate
|
|
{
|
|
public:
|
|
QSocketNotifier *readNotifier;
|
|
QSocketNotifier *writeNotifier;
|
|
|
|
KSocketPrivate() :
|
|
readNotifier(0), writeNotifier(0)
|
|
{ }
|
|
};
|
|
|
|
// I moved this into here so we could accurately detect the domain, for
|
|
// posterity. Really.
|
|
KSocket::KSocket( int _sock)
|
|
: sock(_sock), d(new KSocketPrivate)
|
|
{
|
|
struct sockaddr_in sin;
|
|
ksocklen_t len = sizeof(sin);
|
|
|
|
memset(&sin, 0, len);
|
|
|
|
// getsockname will fill in all the appropriate details, and
|
|
// since sockaddr_in will exist everywhere and is somewhat compatible
|
|
// with sockaddr_in6, we can use it to avoid needless ifdefs.
|
|
KSocks::self()->getsockname(_sock, (struct sockaddr *)&sin, &len);
|
|
}
|
|
|
|
KSocket::KSocket( const char *_host, unsigned short int _port, int _timeout ) :
|
|
sock( -1 ), d(new KSocketPrivate)
|
|
{
|
|
connect( _host, _port, _timeout );
|
|
}
|
|
|
|
KSocket::KSocket( const char *_path ) :
|
|
sock( -1 ), d(new KSocketPrivate)
|
|
{
|
|
connect( _path );
|
|
}
|
|
|
|
void KSocket::enableRead( bool _state )
|
|
{
|
|
if ( _state )
|
|
{
|
|
if ( !d->readNotifier )
|
|
{
|
|
d->readNotifier = new QSocketNotifier( sock, QSocketNotifier::Read );
|
|
QObject::connect( d->readNotifier, SIGNAL( activated(int) ), this, SLOT( slotRead(int) ) );
|
|
}
|
|
else
|
|
d->readNotifier->setEnabled( true );
|
|
}
|
|
else if ( d->readNotifier )
|
|
d->readNotifier->setEnabled( false );
|
|
}
|
|
|
|
void KSocket::enableWrite( bool _state )
|
|
{
|
|
if ( _state )
|
|
{
|
|
if ( !d->writeNotifier )
|
|
{
|
|
d->writeNotifier = new QSocketNotifier( sock, QSocketNotifier::Write );
|
|
QObject::connect( d->writeNotifier, SIGNAL( activated(int) ), this,
|
|
SLOT( slotWrite(int) ) );
|
|
}
|
|
else
|
|
d->writeNotifier->setEnabled( true );
|
|
}
|
|
else if ( d->writeNotifier )
|
|
d->writeNotifier->setEnabled( false );
|
|
}
|
|
|
|
void KSocket::slotRead( int )
|
|
{
|
|
char buffer[2];
|
|
|
|
int n = recv( sock, buffer, 1, MSG_PEEK );
|
|
if ( n <= 0 )
|
|
emit closeEvent( this );
|
|
else
|
|
emit readEvent( this );
|
|
}
|
|
|
|
void KSocket::slotWrite( int )
|
|
{
|
|
emit writeEvent( this );
|
|
}
|
|
|
|
/*
|
|
* Connects the PF_UNIX domain socket to _path.
|
|
*/
|
|
bool KSocket::connect( const char *_path )
|
|
{
|
|
KExtendedSocket ks(QString::null, _path, KExtendedSocket::unixSocket);
|
|
|
|
ks.connect();
|
|
sock = ks.fd();
|
|
ks.release();
|
|
|
|
return sock >= 0;
|
|
}
|
|
|
|
/*
|
|
* Connects the socket to _host, _port.
|
|
*/
|
|
bool KSocket::connect( const QString& _host, unsigned short int _port, int _timeout )
|
|
{
|
|
KExtendedSocket ks(_host, _port, KExtendedSocket::inetSocket);
|
|
ks.setTimeout(_timeout, 0);
|
|
|
|
ks.connect();
|
|
sock = ks.fd();
|
|
ks.release();
|
|
|
|
return sock >= 0;
|
|
}
|
|
|
|
// only for doxygen - the define is always true as defined above
|
|
#ifdef KSOCK_NO_BROKEN
|
|
unsigned long KSocket::ipv4_addr()
|
|
{
|
|
unsigned long retval = 0;
|
|
KSocketAddress *sa = KExtendedSocket::peerAddress(sock);
|
|
if (sa == NULL)
|
|
return 0;
|
|
|
|
if (sa->address() != NULL && (sa->address()->sa_family == PF_INET
|
|
#ifdef PF_INET6
|
|
|| sa->address()->sa_family == PF_INET6
|
|
#endif
|
|
))
|
|
{
|
|
KInetSocketAddress *ksin = (KInetSocketAddress*)sa;
|
|
const sockaddr_in *sin = ksin->addressV4();
|
|
if (sin != NULL)
|
|
retval = sin->sin_addr.s_addr;
|
|
}
|
|
delete sa;
|
|
return retval;
|
|
}
|
|
|
|
bool KSocket::initSockaddr (ksockaddr_in *server_name, const char *hostname, unsigned short int port, int domain)
|
|
{
|
|
// This function is now IPv4 only
|
|
// if you want something better, you should use KExtendedSocket::lookup yourself
|
|
|
|
kdWarning(170) << "deprecated KSocket::initSockaddr called" << endl;
|
|
|
|
if (domain != PF_INET)
|
|
return false;
|
|
|
|
QPtrList<KAddressInfo> list = KExtendedSocket::lookup(hostname, QString::number(port),
|
|
KExtendedSocket::ipv4Socket);
|
|
list.setAutoDelete(true);
|
|
|
|
if (list.isEmpty())
|
|
return false;
|
|
|
|
memset(server_name, 0, sizeof(*server_name));
|
|
|
|
// We are sure that only KInetSocketAddress objects are in the list
|
|
KInetSocketAddress *sin = (KInetSocketAddress*)list.getFirst()->address();
|
|
if (sin == NULL)
|
|
return false;
|
|
|
|
memcpy(server_name, sin->addressV4(), sizeof(*server_name));
|
|
kdDebug(170) << "KSocket::initSockaddr: returning " << sin->pretty() << endl;
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
KSocket::~KSocket()
|
|
{
|
|
// Coolo says delete 0 is ok :) -thiago
|
|
delete d->readNotifier;
|
|
delete d->writeNotifier;
|
|
|
|
delete d;
|
|
|
|
if (sock != -1) {
|
|
::close( sock );
|
|
}
|
|
}
|
|
|
|
class KServerSocketPrivate
|
|
{
|
|
public:
|
|
bool bind;
|
|
QCString path;
|
|
unsigned short int port;
|
|
KExtendedSocket *ks;
|
|
};
|
|
|
|
|
|
KServerSocket::KServerSocket( const char *_path, bool _bind ) :
|
|
sock( -1 )
|
|
{
|
|
d = new KServerSocketPrivate();
|
|
d->bind = _bind;
|
|
|
|
init ( _path );
|
|
}
|
|
|
|
KServerSocket::KServerSocket( unsigned short int _port, bool _bind ) :
|
|
sock( -1 )
|
|
{
|
|
d = new KServerSocketPrivate();
|
|
d->bind = _bind;
|
|
|
|
init ( _port );
|
|
}
|
|
|
|
bool KServerSocket::init( const char *_path )
|
|
{
|
|
unlink(_path );
|
|
d->path = _path;
|
|
|
|
KExtendedSocket *ks = new KExtendedSocket(QString::null, _path, KExtendedSocket::passiveSocket |
|
|
KExtendedSocket::unixSocket);
|
|
d->ks = ks;
|
|
|
|
if (d->bind)
|
|
return bindAndListen();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool KServerSocket::init( unsigned short int _port )
|
|
{
|
|
d->port = _port;
|
|
KExtendedSocket *ks;
|
|
ks = new KExtendedSocket(QString::null, _port, KExtendedSocket::passiveSocket |
|
|
KExtendedSocket::inetSocket);
|
|
d->ks = ks;
|
|
|
|
if (d->bind)
|
|
return bindAndListen();
|
|
return true;
|
|
}
|
|
|
|
bool KServerSocket::bindAndListen()
|
|
{
|
|
if (d == NULL || d->ks == NULL)
|
|
return false;
|
|
|
|
|
|
int ret = d->ks->listen( SOMAXCONN );
|
|
if (ret < 0)
|
|
{
|
|
kdWarning(170) << "Error listening on socket: " << ret << "\n";
|
|
delete d->ks;
|
|
d->ks = NULL;
|
|
sock = -1;
|
|
return false;
|
|
}
|
|
|
|
|
|
sock = d->ks->fd();
|
|
|
|
connect( d->ks->readNotifier(), SIGNAL( activated(int) ), this, SLOT( slotAccept(int) ) );
|
|
return true;
|
|
}
|
|
|
|
|
|
unsigned short int KServerSocket::port()
|
|
{
|
|
if (d == NULL || d->ks == NULL || sock == -1)
|
|
return 0;
|
|
const KSocketAddress *sa = d->ks->localAddress();
|
|
if (sa == NULL)
|
|
return 0;
|
|
|
|
// we can use sockaddr_in here even if it isn't IPv4
|
|
sockaddr_in *sin = (sockaddr_in*)sa->address();
|
|
|
|
if (sin->sin_family == PF_INET)
|
|
// correct family
|
|
return sin->sin_port;
|
|
#ifdef PF_INET6
|
|
else if (sin->sin_family == PF_INET6)
|
|
{
|
|
kde_sockaddr_in6 *sin6 = (kde_sockaddr_in6*)sin;
|
|
return sin6->sin6_port;
|
|
}
|
|
#endif
|
|
return 0; // not a port we know
|
|
}
|
|
|
|
unsigned long KServerSocket::ipv4_addr()
|
|
{
|
|
if (d == NULL || d->ks == NULL || sock == -1)
|
|
return 0;
|
|
const KSocketAddress *sa = d->ks->localAddress();
|
|
|
|
const sockaddr_in *sin = (sockaddr_in*)sa->address();
|
|
|
|
if (sin->sin_family == PF_INET)
|
|
// correct family
|
|
return ntohl(sin->sin_addr.s_addr);
|
|
#ifdef PF_INET6
|
|
else if (sin->sin_family == PF_INET6)
|
|
{
|
|
KInetSocketAddress *ksin = (KInetSocketAddress*)sa;
|
|
sin = ksin->addressV4();
|
|
if (sin != NULL)
|
|
return sin->sin_addr.s_addr;
|
|
}
|
|
#endif
|
|
return 0; // this is dumb, isn't it?
|
|
}
|
|
|
|
void KServerSocket::slotAccept( int )
|
|
{
|
|
if (d == NULL || d->ks == NULL || sock == -1)
|
|
return; // nothing!
|
|
|
|
KExtendedSocket *s;
|
|
if (d->ks->accept(s) < 0)
|
|
{
|
|
kdWarning(170) << "Error accepting\n";
|
|
return;
|
|
}
|
|
|
|
int new_sock = s->fd();
|
|
s->release(); // we're getting rid of the KExtendedSocket
|
|
delete s;
|
|
|
|
emit accepted( new KSocket( new_sock ) );
|
|
}
|
|
|
|
KServerSocket::~KServerSocket()
|
|
{
|
|
if (d != NULL)
|
|
{
|
|
if (d->ks != NULL)
|
|
delete d->ks;
|
|
delete d;
|
|
}
|
|
// deleting d->ks closes the socket
|
|
// ::close( sock );
|
|
}
|
|
|
|
#include "ksock.moc"
|