📄 socket.cxx
字号:
/*
* socket.cxx
*
* Berkley sockets classes implementation
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Revision: 19008 $
* $Author: rjongbloed $
* $Date: 2007-11-29 09:17:41 +0000 (Thu, 29 Nov 2007) $
*/
#pragma implementation "sockets.h"
#pragma implementation "socket.h"
#pragma implementation "ipsock.h"
#pragma implementation "udpsock.h"
#pragma implementation "tcpsock.h"
#pragma implementation "ipdsock.h"
#pragma implementation "ethsock.h"
#pragma implementation "qos.h"
#include <ptlib.h>
#include <ptlib/sockets.h>
#include <map>
#include <ptlib/pstring.h>
#if defined(SIOCGENADDR)
#define SIO_Get_MAC_Address SIOCGENADDR
#define ifr_macaddr ifr_ifru.ifru_enaddr
#elif defined(SIOCGIFHWADDR)
#define SIO_Get_MAC_Address SIOCGIFHWADDR
#define ifr_macaddr ifr_hwaddr.sa_data
#endif
#if defined(P_FREEBSD) || defined(P_OPENBSD) || defined(P_NETBSD) || defined(P_SOLARIS) || defined(P_MACOSX) || defined(P_MACOS) || defined(P_IRIX) || defined(P_VXWORKS) || defined(P_RTEMS) || defined(P_QNX)
#define ifr_netmask ifr_addr
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#if !defined(P_QNX)
#include <netinet/if_ether.h>
#endif
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#endif
#if defined(P_FREEBSD) || defined(P_OPENBSD) || defined(P_NETBSD) || defined(P_MACOSX) || defined(P_MACOS) || defined(P_QNX)
#include <sys/sysctl.h>
#endif
#ifdef P_RTEMS
#include <bsp.h>
#endif
#ifdef __BEOS__
#include <posix/sys/ioctl.h> // for FIONBIO
#include <be/bone/net/if.h> // for ifconf
#include <be/bone/sys/sockio.h> // for SIOCGI*
#endif
#if defined(P_FREEBSD) || defined(P_OPENBSD) || defined(P_NETBSD) || defined(P_MACOSX) || defined(P_VXWORKS) || defined(P_RTEMS) || defined(P_QNX)
// Define _SIZEOF_IFREQ for platforms (eg OpenBSD) which do not have it.
#ifndef _SIZEOF_ADDR_IFREQ
#define _SIZEOF_ADDR_IFREQ(ifr) \
((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
(sizeof(struct ifreq) - sizeof(struct sockaddr) + \
(ifr).ifr_addr.sa_len) : sizeof(struct ifreq))
#endif
#endif
int PX_NewHandle(const char *, int);
#ifdef P_VXWORKS
// VxWorks variant of inet_ntoa() allocates INET_ADDR_LEN bytes via malloc
// BUT DOES NOT FREE IT !!! Use inet_ntoa_b() instead.
#define INET_ADDR_LEN 18
extern "C" void inet_ntoa_b(struct in_addr inetAddress, char *pString);
#endif // P_VXWORKS
//////////////////////////////////////////////////////////////////////////////
// P_fd_set
void P_fd_set::Construct()
{
max_fd = PProcess::Current().GetMaxHandles();
set = (fd_set *)malloc((max_fd+7)>>3);
}
void P_fd_set::Zero()
{
if (PAssertNULL(set) != NULL)
memset(set, 0, (max_fd+7)>>3);
}
//////////////////////////////////////////////////////////////////////////////
PSocket::~PSocket()
{
os_close();
}
int PSocket::os_close()
{
if (os_handle < 0)
return -1;
// send a shutdown to the other end
::shutdown(os_handle, 2);
return PXClose();
}
static int SetNonBlocking(int fd)
{
if (fd < 0)
return -1;
// Set non-blocking so we can use select calls to break I/O block on close
int cmd = 1;
#if defined(P_VXWORKS)
if (::ioctl(fd, FIONBIO, &cmd) == 0)
#else
if (::ioctl(fd, FIONBIO, &cmd) == 0 && ::fcntl(fd, F_SETFD, 1) == 0)
#endif
return fd;
::close(fd);
return -1;
}
int PSocket::os_socket(int af, int type, int protocol)
{
// attempt to create a socket
return SetNonBlocking(PX_NewHandle(GetClass(), ::socket(af, type, protocol)));
}
PBoolean PSocket::os_connect(struct sockaddr * addr, PINDEX size)
{
int val;
do {
val = ::connect(os_handle, addr, size);
} while (val != 0 && errno == EINTR);
if (val == 0 || errno != EINPROGRESS)
return ConvertOSError(val);
if (!PXSetIOBlock(PXConnectBlock, readTimeout))
return PFalse;
// A successful select() call does not necessarily mean the socket connected OK.
int optval = -1;
socklen_t optlen = sizeof(optval);
getsockopt(os_handle, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen);
if (optval != 0) {
errno = optval;
return ConvertOSError(-1);
}
return PTrue;
}
PBoolean PSocket::os_accept(PSocket & listener, struct sockaddr * addr, PINDEX * size)
{
if (!listener.PXSetIOBlock(PXAcceptBlock, listener.GetReadTimeout()))
return SetErrorValues(listener.GetErrorCode(), listener.GetErrorNumber());
#if defined(E_PROTO)
for (;;) {
int new_fd = ::accept(listener.GetHandle(), addr, (socklen_t *)size);
if (new_fd >= 0)
return ConvertOSError(os_handle = SetNonBlocking(new_fd));
if (errno != EPROTO)
return ConvertOSError(-1);
PTRACE(3, "PWLib\tAccept on " << sock << " failed with EPROTO - retrying");
}
#else
return ConvertOSError(os_handle = SetNonBlocking(::accept(listener.GetHandle(), addr, (socklen_t *)size)));
#endif
}
#if !defined(P_PTHREADS) && !defined(P_MAC_MPTHREADS) && !defined(__BEOS__)
PChannel::Errors PSocket::Select(SelectList & read,
SelectList & write,
SelectList & except,
const PTimeInterval & timeout)
{
PINDEX i, j;
PINDEX nextfd = 0;
int maxfds = 0;
Errors lastError = NoError;
PThread * unblockThread = PThread::Current();
P_fd_set fds[3];
SelectList * list[3] = { &read, &write, &except };
for (i = 0; i < 3; i++) {
for (j = 0; j < list[i]->GetSize(); j++) {
PSocket & socket = (*list[i])[j];
if (!socket.IsOpen())
lastError = NotOpen;
else {
int h = socket.GetHandle();
fds[i] += h;
if (h > maxfds)
maxfds = h;
}
socket.px_selectMutex[i].Wait();
socket.px_selectThread[i] = unblockThread;
}
}
if (lastError == NoError) {
P_timeval tval = timeout;
int result = ::select(maxfds+1,
(fd_set *)fds[0],
(fd_set *)fds[1],
(fd_set *)fds[2],
tval);
int osError;
(void)ConvertOSError(result, lastError, osError);
}
for (i = 0; i < 3; i++) {
for (j = 0; j < list[i]->GetSize(); j++) {
PSocket & socket = (*list[i])[j];
socket.px_selectThread[i] = NULL;
socket.px_selectMutex[i].Signal();
if (lastError == NoError) {
int h = socket.GetHandle();
if (h < 0)
lastError = Interrupted;
else if (!fds[i].IsPresent(h))
list[i]->RemoveAt(j--);
}
}
}
return lastError;
}
#else
PChannel::Errors PSocket::Select(SelectList & read,
SelectList & write,
SelectList & except,
const PTimeInterval & timeout)
{
PINDEX i, j;
int maxfds = 0;
Errors lastError = NoError;
PThread * unblockThread = PThread::Current();
int unblockPipe = unblockThread->unblockPipe[0];
P_fd_set fds[3];
SelectList * list[3] = { &read, &write, &except };
for (i = 0; i < 3; i++) {
for (j = 0; j < list[i]->GetSize(); j++) {
PSocket & socket = (*list[i])[j];
if (!socket.IsOpen())
lastError = NotOpen;
else {
int h = socket.GetHandle();
fds[i] += h;
if (h > maxfds)
maxfds = h;
}
socket.px_selectMutex[i].Wait();
socket.px_selectThread[i] = unblockThread;
}
}
int result = -1;
if (lastError == NoError) {
fds[0] += unblockPipe;
if (unblockPipe > maxfds)
maxfds = unblockPipe;
P_timeval tval = timeout;
do {
result = ::select(maxfds+1, (fd_set *)fds[0], (fd_set *)fds[1], (fd_set *)fds[2], tval);
} while (result < 0 && errno == EINTR);
int osError;
if (ConvertOSError(result, lastError, osError)) {
if (fds[0].IsPresent(unblockPipe)) {
PTRACE(6, "PWLib\tSelect unblocked fd=" << unblockPipe);
BYTE ch;
::read(unblockPipe, &ch, 1);
lastError = Interrupted;
}
}
}
for (i = 0; i < 3; i++) {
for (j = 0; j < list[i]->GetSize(); j++) {
PSocket & socket = (*list[i])[j];
socket.px_selectThread[i] = NULL;
socket.px_selectMutex[i].Signal();
if (lastError == NoError) {
int h = socket.GetHandle();
if (h < 0)
lastError = Interrupted;
else if (!fds[i].IsPresent(h))
list[i]->RemoveAt(j--);
}
}
}
return lastError;
}
#endif
PIPSocket::Address::Address(DWORD dw)
{
operator=(dw);
}
PIPSocket::Address & PIPSocket::Address::operator=(DWORD dw)
{
if (dw == 0) {
version = 0;
memset(&v, 0, sizeof(v));
}
else {
version = 4;
v.four.s_addr = dw;
}
return *this;
}
PIPSocket::Address::operator DWORD() const
{
return version != 4 ? 0 : (DWORD)v.four.s_addr;
}
BYTE PIPSocket::Address::Byte1() const
{
return *(((BYTE *)&v.four.s_addr)+0);
}
BYTE PIPSocket::Address::Byte2() const
{
return *(((BYTE *)&v.four.s_addr)+1);
}
BYTE PIPSocket::Address::Byte3() const
{
return *(((BYTE *)&v.four.s_addr)+2);
}
BYTE PIPSocket::Address::Byte4() const
{
return *(((BYTE *)&v.four.s_addr)+3);
}
PIPSocket::Address::Address(BYTE b1, BYTE b2, BYTE b3, BYTE b4)
{
version = 4;
BYTE * p = (BYTE *)&v.four.s_addr;
p[0] = b1;
p[1] = b2;
p[2] = b3;
p[3] = b4;
}
PBoolean PIPSocket::IsLocalHost(const PString & hostname)
{
if (hostname.IsEmpty())
return PTrue;
if (hostname *= "localhost")
return PTrue;
// lookup the host address using inet_addr, assuming it is a "." address
Address addr = hostname;
if (addr.IsLoopback()) // Is 127.0.0.1
return PTrue;
if (!addr.IsValid())
return PFalse;
if (!GetHostAddress(hostname, addr))
return PFalse;
#if P_HAS_IPV6
{
FILE * file;
int dummy;
int addr6[16];
char ifaceName[255];
PBoolean found = PFalse;
if ((file = fopen("/proc/net/if_inet6", "r")) != NULL) {
while (!found && (fscanf(file, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %x %x %x %x %255s\n",
&addr6[0], &addr6[1], &addr6[2], &addr6[3],
&addr6[4], &addr6[5], &addr6[6], &addr6[7],
&addr6[8], &addr6[9], &addr6[10], &addr6[11],
&addr6[12], &addr6[13], &addr6[14], &addr6[15],
&dummy, &dummy, &dummy, &dummy, ifaceName) != EOF)) {
Address ip6addr(
psprintf("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
addr6[0], addr6[1], addr6[2], addr6[3],
addr6[4], addr6[5], addr6[6], addr6[7],
addr6[8], addr6[9], addr6[10], addr6[11],
addr6[12], addr6[13], addr6[14], addr6[15]
)
);
found = (ip6addr *= addr);
}
fclose(file);
}
if (found)
return PTrue;
}
#endif
// check IPV4 addresses
PUDPSocket sock;
PBYTEArray buffer;
struct ifconf ifConf;
#ifdef SIOCGIFNUM
int ifNum;
PAssert(::ioctl(sock.GetHandle(), SIOCGIFNUM, &ifNum) >= 0, "could not do ioctl for ifNum");
ifConf.ifc_len = ifNum * sizeof(ifreq);
#else
ifConf.ifc_len = 100 * sizeof(ifreq); // That's a LOT of interfaces!
#endif
ifConf.ifc_req = (struct ifreq *)buffer.GetPointer(ifConf.ifc_len);
if (ioctl(sock.GetHandle(), SIOCGIFCONF, &ifConf) >= 0) {
void * ifEndList = (char *)ifConf.ifc_req + ifConf.ifc_len;
ifreq * ifName = ifConf.ifc_req;
while (ifName < ifEndList) {
struct ifreq ifReq;
memcpy(&ifReq, ifName, sizeof(ifreq));
if (ioctl(sock.GetHandle(), SIOCGIFFLAGS, &ifReq) >= 0) {
int flags = ifReq.ifr_flags;
if ((flags & IFF_UP) && ioctl(sock.GetHandle(), SIOCGIFADDR, &ifReq) >= 0) {
sockaddr_in * sin = (sockaddr_in *)&ifReq.ifr_addr;
PIPSocket::Address address = sin->sin_addr;
if (addr *= address)
return PTrue;
}
}
#if defined(P_FREEBSD) || defined(P_OPENBSD) || defined(P_NETBSD) || defined(P_MACOSX) || defined(P_VXWORKS) || defined(P_RTEMS) || defined(P_QNX)
// move the ifName pointer along to the next ifreq entry
ifName = (struct ifreq *)((char *)ifName + _SIZEOF_ADDR_IFREQ(*ifName));
#else
ifName++;
#endif
}
}
return PFalse;
}
////////////////////////////////////////////////////////////////
//
// PTCPSocket
//
PBoolean PTCPSocket::Read(void * buf, PINDEX maxLen)
{
lastReadCount = 0;
// wait until select indicates there is data to read, or until
// a timeout occurs
if (!PXSetIOBlock(PXReadBlock, readTimeout))
return PFalse;
// attempt to read out of band data
char buffer[32];
int ooblen;
while ((ooblen = ::recv(os_handle, buffer, sizeof(buffer), MSG_OOB)) > 0)
OnOutOfBand(buffer, ooblen);
// attempt to read non-out of band data
int r = ::recv(os_handle, (char *)buf, maxLen, 0);
if (!ConvertOSError(r, LastReadError))
return PFalse;
lastReadCount = r;
return lastReadCount > 0;
}
#if P_HAS_RECVMSG
PBoolean PSocket::os_recvfrom(
void * buf, // Data to be written as URGENT TCP data.
PINDEX len, // Number of bytes pointed to by <CODE>buf</CODE>.
int flags,
sockaddr * addr, // Address from which the datagram was received.
PINDEX * addrlen)
{
lastReadCount = 0;
if (!PXSetIOBlock(PXReadBlock, readTimeout))
return PFalse;
// if we don't care what interface the packet arrives on, then don't bother getting the information
if (!catchReceiveToAddr) {
int r = ::recvfrom(os_handle, (char *)buf, len, flags, (sockaddr *)addr, (socklen_t *)addrlen);
if (!ConvertOSError(r, LastReadError))
return PFalse;
lastReadCount = r;
return lastReadCount > 0;
}
msghdr readData;
memset(&readData, 0, sizeof(readData));
readData.msg_name = addr;
readData.msg_namelen = *addrlen;
iovec readVector;
readVector.iov_base = buf;
readVector.iov_len = len;
readData.msg_iov = &readVector;
readData.msg_iovlen = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -