mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
1548 lines
45 KiB
C++
1548 lines
45 KiB
C++
// ============================================================================
|
|
//
|
|
// = LIBRARY
|
|
// ULib - c++ library
|
|
//
|
|
// = FILENAME
|
|
// socket_ext.cpp
|
|
//
|
|
// = AUTHOR
|
|
// Stefano Casazza
|
|
//
|
|
// ============================================================================
|
|
|
|
#include <ulib/file.h>
|
|
#include <ulib/notifier.h>
|
|
#include <ulib/net/client/client.h>
|
|
#include <ulib/utility/interrupt.h>
|
|
#include <ulib/net/server/server.h>
|
|
|
|
#ifdef HAVE_SYS_SENDFILE_H
|
|
# ifndef HAVE_SENDFILE64
|
|
# undef __USE_FILE_OFFSET64
|
|
# endif
|
|
# include <sys/sendfile.h>
|
|
# ifndef HAVE_SENDFILE64
|
|
# define __USE_FILE_OFFSET64
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef _MSWINDOWS_
|
|
# include <ws2tcpip.h>
|
|
#elif defined(HAVE_NETPACKET_PACKET_H)
|
|
# include <net/if.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
#ifdef HAVE_ARPA_INET_H
|
|
# include <net/if_arp.h>
|
|
#endif
|
|
|
|
#ifdef USE_C_ARES
|
|
# include <ares.h>
|
|
#endif
|
|
|
|
/**
|
|
* Socket I/O - read while not received almost count data
|
|
*
|
|
* @timeoutMS specified the timeout value, in milliseconds.
|
|
* A negative value indicates no timeout, i.e. an infinite wait
|
|
* @time_limit specified the maximum execution time, in seconds. If set to zero, no time limit is imposed
|
|
*/
|
|
|
|
bool USocketExt::read(USocket* sk, UString& buffer, uint32_t count, int timeoutMS, uint32_t time_limit)
|
|
{
|
|
U_TRACE(0, "USocketExt::read(%p,%V,%u,%d,%u)", sk, buffer.rep, count, timeoutMS, time_limit)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(sk)
|
|
U_INTERNAL_ASSERT(sk->isConnected())
|
|
|
|
U_DUMP("bssl = %b blocking = %b", sk->isSSLActive(), sk->isBlocking())
|
|
|
|
char* ptr;
|
|
long timeout = 0;
|
|
int byte_read = 0;
|
|
ssize_t value; // = -1;
|
|
uint32_t start = buffer.size(), // NB: read buffer can have previous data...
|
|
ncount = buffer.space(),
|
|
chunk = count;
|
|
|
|
if (LIKELY(chunk < U_CAPACITY)) chunk = U_CAPACITY;
|
|
|
|
if (UNLIKELY(ncount < chunk))
|
|
{
|
|
if (sk == UServer_Base::csocket) { UClientImage_Base::manageReadBufferResize(chunk); } // start = buffer.size(); }
|
|
else if (sk == UClient_Base::csocket) UClient_Base::resize_response_buffer(chunk);
|
|
else UString::_reserve(buffer, buffer.getReserveNeed(chunk));
|
|
|
|
ncount = buffer.space();
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(ncount, 0)
|
|
}
|
|
|
|
ptr = buffer.c_pointer(start);
|
|
|
|
read:
|
|
if (sk->isBlocking() &&
|
|
timeoutMS != 0)
|
|
{
|
|
# ifndef USE_LIBSSL
|
|
if (errno = 0, UNotifier::waitForRead(sk->iSockDesc, timeoutMS) != 1) goto error;
|
|
# else
|
|
/**
|
|
* When packets in SSL arrive at a destination, they are pulled off the socket in chunks of sizes
|
|
* controlled by the encryption protocol being used, decrypted, and placed in SSL-internal buffers.
|
|
* The buffer content is then transferred to the application program through SSL_read(). If you've
|
|
* read only part of the decrypted data, there will still be pending input data on the SSL connection,
|
|
* but it won't show up on the underlying file descriptor via select(). Your code needs to call
|
|
* SSL_pending() explicitly to see if there is any pending data to be read
|
|
*/
|
|
|
|
U_DUMP("sk->pending() = %u", ((USSLSocket*)sk)->pending())
|
|
|
|
/**
|
|
* What I see by myself is that for blocking socket SSL_read() can return some data though
|
|
* it won't show up on the underlying file descriptor via select() while SSL_pending() return 0...
|
|
*/
|
|
|
|
if (sk->isSSLActive() == false && // NB: without this csp test fail: we have a timeout(10 seconds) when we try to read SOAP body response...
|
|
(errno = 0, UNotifier::waitForRead(sk->iSockDesc, timeoutMS) != 1))
|
|
{
|
|
goto error;
|
|
}
|
|
# endif
|
|
}
|
|
|
|
value = sk->recv(ptr + byte_read, ncount);
|
|
|
|
if (value <= 0)
|
|
{
|
|
if (value == -1)
|
|
{
|
|
error: U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
if (errno != EAGAIN)
|
|
{
|
|
if (U_ClientImage_parallelization != U_PARALLELIZATION_CHILD)
|
|
{
|
|
if (errno != ECONNRESET &&
|
|
sk == UServer_Base::csocket)
|
|
{
|
|
sk->iState = USocket::BROKEN;
|
|
}
|
|
|
|
sk->abortive_close();
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
if (timeoutMS != 0)
|
|
{
|
|
if (UNotifier::waitForRead(sk->iSockDesc, timeoutMS) == 1) goto read;
|
|
|
|
sk->iState |= USocket::TIMEOUT;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("sk->state = %d %B", sk->iState, sk->iState)
|
|
}
|
|
else
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(value, 0)
|
|
|
|
errno = 0;
|
|
|
|
if (byte_read == 0 ||
|
|
sk->shutdown(SHUT_RD) == false)
|
|
{
|
|
U_INTERNAL_DUMP("byte_read = %d errno = %d", byte_read, errno)
|
|
|
|
if (U_ClientImage_parallelization != U_PARALLELIZATION_CHILD) sk->abortive_close();
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
UClientImage_Base::setCloseConnection();
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
byte_read += value;
|
|
|
|
U_INTERNAL_DUMP("byte_read = %d count = %u", byte_read, count)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(byte_read, 0)
|
|
|
|
if (byte_read < (int)count)
|
|
{
|
|
U_INTERNAL_ASSERT_DIFFERS(count, U_SINGLE_READ)
|
|
|
|
if (time_limit &&
|
|
sk->checkTime(time_limit, timeout) == false) // NB: may be we are attacked by a "slow loris"... http://lwn.net/Articles/337853/
|
|
{
|
|
sk->iState |= USocket::TIMEOUT;
|
|
|
|
goto done;
|
|
}
|
|
|
|
ncount -= value;
|
|
|
|
goto read;
|
|
}
|
|
|
|
if (value == (ssize_t)ncount)
|
|
{
|
|
// NB: may be there are available more bytes to read...
|
|
|
|
buffer.rep->_length = start + byte_read;
|
|
|
|
if (sk == UServer_Base::csocket) { UClientImage_Base::manageReadBufferResize(ncount * 2); } // start = buffer.size(); }
|
|
else if (sk == UClient_Base::csocket) UClient_Base::resize_response_buffer(ncount * 2);
|
|
else UString::_reserve(buffer, buffer.getReserveNeed(ncount * 2));
|
|
|
|
ptr = buffer.c_pointer(start);
|
|
ncount = buffer.space();
|
|
timeoutMS = 0;
|
|
|
|
goto read;
|
|
}
|
|
|
|
#ifdef U_EPOLLET_POSTPONE_STRATEGY
|
|
if (UNotifier::bepollet == false)
|
|
#endif
|
|
{
|
|
#if !defined(ENABLE_THREAD) || !defined(U_LOG_DISABLE) || defined(USE_LIBZ)
|
|
if (sk->isBlocking() == false)
|
|
{
|
|
/**
|
|
* Edge trigger (EPOLLET) simply means (unless you've used EPOLLONESHOT) that you'll get 1 event when something
|
|
* enters the (kernel) buffer. Thus, if you get 1 EPOLLIN event and do nothing about it, you'll get another
|
|
* EPOLLIN the next time some data arrives on that descriptor - if no new data arrives, you will not get an
|
|
* event though, even if you didn't read any data as indicated by the first event. Well, to put it succinctly,
|
|
* EPOLLONESHOT just means that if you don't read the data you're supposed to read, they will be discarded.
|
|
* Normally, you'd be notified with an event for the same data if you don't read them. With EPOLLONESHOT, however,
|
|
* not reading the data is perfectly legal and they will be just ignored. Hence, no further events will be generated.
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* The suggested way to use epoll as an edge-triggered (EPOLLET) interface is as follows:
|
|
*
|
|
* 1) with nonblocking file descriptors
|
|
* 2) by waiting for an event only after read(2) or write(2) return EAGAIN
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* Edge-triggered semantics allow a more efficient internal implementation than level-triggered semantics.
|
|
*
|
|
* see: https://raw.githubusercontent.com/dankamongmen/libtorque/master/doc/mteventqueues
|
|
*/
|
|
|
|
buffer.rep->_length = start + byte_read;
|
|
|
|
ncount = buffer.space();
|
|
timeoutMS = 0;
|
|
|
|
goto read;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
done:
|
|
U_INTERNAL_DUMP("byte_read = %d", byte_read)
|
|
|
|
if (byte_read)
|
|
{
|
|
start += byte_read;
|
|
|
|
if (start > buffer.size()) buffer.size_adjust_force(start); // NB: we force because the string can be referenced...
|
|
|
|
if (byte_read >= (int)count &&
|
|
sk->iState != USocket::CLOSE)
|
|
{
|
|
U_RETURN(true);
|
|
}
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
/**
|
|
* Socket I/O - read while not received token, return position of token in buffer read
|
|
*
|
|
* @param timeoutMS specified the timeout value, in milliseconds.
|
|
* A negative value indicates no timeout, i.e. an infinite wait
|
|
*/
|
|
|
|
uint32_t USocketExt::readWhileNotToken(USocket* sk, UString& buffer, const char* token, uint32_t token_len, int timeoutMS)
|
|
{
|
|
U_TRACE(0, "USocketExt::readWhileNotToken(%p,%V,%.*S,%u,%d)", sk, buffer.rep, token_len, token, token_len, timeoutMS)
|
|
|
|
uint32_t start = buffer.size();
|
|
|
|
while (USocketExt::read(sk, buffer, U_SINGLE_READ, timeoutMS))
|
|
{
|
|
uint32_t pos_token = buffer.find(token, start, token_len);
|
|
|
|
if (pos_token != U_NOT_FOUND) U_RETURN(pos_token);
|
|
|
|
U_ASSERT_MAJOR(buffer.size(), token_len)
|
|
|
|
start = buffer.size() - token_len;
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
// write data
|
|
|
|
uint32_t USocketExt::write(USocket* sk, const char* ptr, uint32_t count, int timeoutMS)
|
|
{
|
|
U_TRACE(0, "USocketExt::write(%p,%.*S,%u,%d)", sk, count, ptr, count, timeoutMS)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(sk)
|
|
U_INTERNAL_ASSERT_MAJOR(count, 0)
|
|
U_INTERNAL_ASSERT(sk->isConnected())
|
|
|
|
U_DUMP("bssl = %b blocking = %b", sk->isSSLActive(), sk->isBlocking())
|
|
|
|
ssize_t value;
|
|
uint32_t byte_written = 0;
|
|
|
|
write:
|
|
/*
|
|
if (sk->isBlocking() &&
|
|
timeoutMS != 0 &&
|
|
(errno = 0, UNotifier::waitForWrite(sk->iSockDesc, timeoutMS) != 1))
|
|
{
|
|
goto error;
|
|
}
|
|
*/
|
|
|
|
value = sk->send(ptr + byte_written, count);
|
|
|
|
if (value <= 0)
|
|
{
|
|
if (value == -1)
|
|
{
|
|
//error:
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
if (errno != EAGAIN) sk->abortive_close();
|
|
else if (timeoutMS != 0)
|
|
{
|
|
if (UNotifier::waitForWrite(sk->iSockDesc, timeoutMS) == 1) goto write;
|
|
|
|
sk->iState |= USocket::TIMEOUT;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("sk->state = %d %B", sk->iState, sk->iState)
|
|
}
|
|
|
|
U_RETURN(byte_written);
|
|
}
|
|
|
|
byte_written += value;
|
|
|
|
U_INTERNAL_DUMP("byte_written = %u", byte_written)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(byte_written, 0)
|
|
|
|
if (byte_written < count)
|
|
{
|
|
count -= value;
|
|
|
|
goto write;
|
|
}
|
|
|
|
U_RETURN(byte_written);
|
|
}
|
|
|
|
// sendfile() copies data between one file descriptor and another. Either or both of these file descriptors may refer to a socket.
|
|
// OUT_FD should be a descriptor opened for writing. POFFSET is a pointer to a variable holding the input file pointer position from
|
|
// which sendfile() will start reading data. When sendfile() returns, this variable will be set to the offset of the byte following
|
|
// the last byte that was read. COUNT is the number of bytes to copy between file descriptors. Because this copying is done within
|
|
// the kernel, sendfile() does not need to spend time transferring data to and from user space
|
|
|
|
uint32_t USocketExt::sendfile(USocket* sk, int in_fd, off_t* poffset, off_t count, int timeoutMS)
|
|
{
|
|
U_TRACE(1, "USocketExt::sendfile(%p,%d,%p,%I,%d)", sk, in_fd, poffset, count, timeoutMS)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(sk)
|
|
U_INTERNAL_ASSERT_MAJOR(count, 0)
|
|
U_INTERNAL_ASSERT(sk->isConnected())
|
|
|
|
U_DUMP("bssl = %b blocking = %b", sk->isSSLActive(), sk->isBlocking())
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(sk->isSSLActive(), false)
|
|
|
|
#if defined(HAVE_MACOSX_SENDFILE)
|
|
off_t len;
|
|
#endif
|
|
ssize_t value;
|
|
uint32_t byte_written = 0;
|
|
|
|
loop:
|
|
/*
|
|
if (sk->isBlocking() &&
|
|
timeoutMS != 0 &&
|
|
(errno = 0, UNotifier::waitForWrite(sk->iSockDesc, timeoutMS) != 1))
|
|
{
|
|
goto error;
|
|
}
|
|
*/
|
|
|
|
#ifndef HAVE_MACOSX_SENDFILE
|
|
value = U_SYSCALL(sendfile, "%d,%d,%p,%I", sk->getFd(), in_fd, poffset, count);
|
|
#else
|
|
/**
|
|
* struct sf_hdtr {
|
|
* struct iovec *headers; // pointer to header iovecs
|
|
* int hdr_cnt; // number of header iovecs
|
|
* struct iovec *trailers; // pointer to trailer iovecs
|
|
* int trl_cnt; // number of trailer iovecs
|
|
* };
|
|
*
|
|
* int sendfile(int fd, int s, off_t offset, off_t* len, struct sf_hdtr* hdtr, int flags);
|
|
*
|
|
* Since Mac OSX uses the fourth argument as a value-return parameter, success or failure, we need to put the result into value after the call
|
|
*/
|
|
|
|
len = count;
|
|
value = U_SYSCALL(sendfile, "%d,%d,%I,%p,%p,%d", sk->getFd(), in_fd, *poffset, &len, U_NULLPTR, 0);
|
|
|
|
if (value == -1) goto error;
|
|
|
|
poffset += (value = len);
|
|
#endif
|
|
|
|
if (value <= 0)
|
|
{
|
|
if (value == -1)
|
|
{
|
|
//error:
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
if (errno != EAGAIN)
|
|
{
|
|
if (errno == EINTR)
|
|
{
|
|
UInterrupt::checkForEventSignalPending();
|
|
|
|
goto loop;
|
|
}
|
|
|
|
sk->abortive_close();
|
|
}
|
|
else if (timeoutMS != 0)
|
|
{
|
|
if (UNotifier::waitForWrite(sk->iSockDesc, timeoutMS) == 1) goto loop;
|
|
|
|
sk->iState |= USocket::TIMEOUT;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("sk->state = %d %B", sk->iState, sk->iState)
|
|
}
|
|
|
|
U_RETURN(byte_written);
|
|
}
|
|
|
|
byte_written += value;
|
|
|
|
U_INTERNAL_DUMP("byte_written = %d", byte_written)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(byte_written, 0)
|
|
|
|
if (byte_written < count)
|
|
{
|
|
count -= value;
|
|
|
|
goto loop;
|
|
}
|
|
|
|
U_RETURN(byte_written);
|
|
}
|
|
|
|
// write data from multiple buffers
|
|
|
|
uint32_t USocketExt::iov_resize(struct iovec* liov, struct iovec* iov, int iovcnt, uint32_t byte_written)
|
|
{
|
|
U_TRACE(0, "USocketExt::iov_resize(%p,%p,%d,%u)", liov, iov, iovcnt, byte_written)
|
|
|
|
int liovcnt;
|
|
uint32_t idx;
|
|
|
|
for (idx = 0; byte_written >= iov[idx].iov_len; ++idx)
|
|
{
|
|
byte_written -= iov[idx].iov_len;
|
|
// iov[idx].iov_len = 0;
|
|
}
|
|
|
|
liov = iov + idx;
|
|
liovcnt = iovcnt - idx;
|
|
|
|
if (byte_written)
|
|
{
|
|
liov[0].iov_base =
|
|
(char*)iov[0].iov_base + byte_written;
|
|
liov[0].iov_len =
|
|
iov[0].iov_len - byte_written;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("idx = %u liovcnt = %u liov[0].iov_len = %u byte_written = %u", idx, liovcnt, liov[0].iov_len, byte_written)
|
|
|
|
U_INTERNAL_ASSERT_RANGE(0,liovcnt,256)
|
|
U_INTERNAL_ASSERT_MAJOR(liov[0].iov_len, byte_written)
|
|
|
|
U_DUMP_IOVEC(liov,liovcnt)
|
|
|
|
U_RETURN(liovcnt);
|
|
}
|
|
|
|
uint32_t USocketExt::writev(USocket* sk, struct iovec* iov, int iovcnt, uint32_t count, int timeoutMS)
|
|
{
|
|
U_TRACE(0, "USocketExt::writev(%p,%p,%d,%u,%d)", sk, iov, iovcnt, count, timeoutMS)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(sk)
|
|
U_INTERNAL_ASSERT_MAJOR(count, 0)
|
|
U_INTERNAL_ASSERT(sk->isConnected())
|
|
U_INTERNAL_ASSERT_RANGE(0,iovcnt,256)
|
|
|
|
U_DUMP("bssl = %b blocking = %b", sk->isSSLActive(), sk->isBlocking())
|
|
|
|
ssize_t value;
|
|
struct iovec liov[256];
|
|
uint32_t byte_written = 0;
|
|
|
|
loop:
|
|
#ifdef DEBUG
|
|
uint32_t sum = 0;
|
|
|
|
for (int i = 0; i < iovcnt; ++i) sum += iov[i].iov_len;
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(sum, count-byte_written)
|
|
#endif
|
|
|
|
/*
|
|
if (sk->isBlocking() &&
|
|
timeoutMS != 0 &&
|
|
(errno = 0, UNotifier::waitForWrite(sk->iSockDesc, timeoutMS) != 1))
|
|
{
|
|
goto error;
|
|
}
|
|
*/
|
|
|
|
#if defined(USE_LIBSSL) && !defined(_MSWINDOWS_)
|
|
if (sk->isSSLActive())
|
|
#endif
|
|
#if defined(USE_LIBSSL) || defined(_MSWINDOWS_)
|
|
{
|
|
if (iovcnt == 1)
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(iov[0].iov_len, count-byte_written)
|
|
|
|
value = sk->send((const char*)iov[0].iov_base, iov[0].iov_len);
|
|
|
|
goto check;
|
|
}
|
|
|
|
if (count <= U_BUFFER_SIZE) // NB: OpenSSL has no SSL_writev() so we copy several bufs into our buffer (8k) before the SSL_write() call to decrease a SSL overhead...
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(u_buffer_len, 0)
|
|
|
|
if (byte_written) value = sk->send(u_buffer+byte_written, count-byte_written);
|
|
else
|
|
{
|
|
char* ptr = u_buffer;
|
|
|
|
for (int i = 0; i < iovcnt; ++i)
|
|
{
|
|
if (iov[i].iov_len)
|
|
{
|
|
U_MEMCPY(ptr, (const char*)iov[i].iov_base, iov[i].iov_len);
|
|
ptr += iov[i].iov_len;
|
|
}
|
|
}
|
|
|
|
value = sk->send(u_buffer, count);
|
|
}
|
|
|
|
goto check;
|
|
}
|
|
|
|
for (int i = 0; i < iovcnt; ++i)
|
|
{
|
|
int sz = iov[i].iov_len;
|
|
|
|
if (sz)
|
|
{
|
|
value = writev(sk, iov+i, 1, sz, timeoutMS);
|
|
|
|
byte_written += value;
|
|
|
|
if (value < sz) U_RETURN(byte_written);
|
|
}
|
|
}
|
|
|
|
U_RETURN(byte_written);
|
|
}
|
|
#endif
|
|
value = U_SYSCALL(writev, "%d,%p,%d", sk->iSockDesc, iov, iovcnt);
|
|
|
|
#if defined(USE_LIBSSL) || defined(_MSWINDOWS_)
|
|
check:
|
|
#endif
|
|
if (value <= 0)
|
|
{
|
|
if (value == -1)
|
|
{
|
|
//error:
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
if (errno != EAGAIN)
|
|
{
|
|
if (errno == EINTR)
|
|
{
|
|
UInterrupt::checkForEventSignalPending();
|
|
|
|
goto loop;
|
|
}
|
|
|
|
sk->abortive_close();
|
|
}
|
|
else if (timeoutMS != 0)
|
|
{
|
|
if (UNotifier::waitForWrite(sk->iSockDesc, timeoutMS) == 1) goto loop;
|
|
|
|
sk->iState |= USocket::TIMEOUT;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("sk->state = %d %B", sk->iState, sk->iState)
|
|
}
|
|
|
|
U_RETURN(byte_written);
|
|
}
|
|
|
|
byte_written += value;
|
|
|
|
U_INTERNAL_DUMP("byte_written = %u", byte_written)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(byte_written, 0)
|
|
|
|
if (byte_written < count)
|
|
{
|
|
iovcnt = iov_resize(liov, iov, iovcnt, value);
|
|
iov = liov;
|
|
|
|
goto loop;
|
|
}
|
|
|
|
U_RETURN(byte_written);
|
|
}
|
|
|
|
// Send a command to a server and wait for a response (single line)
|
|
|
|
int USocketExt::vsyncCommand(USocket* sk, char* buffer, uint32_t buffer_size, const char* format, uint32_t fmt_size, va_list argp)
|
|
{
|
|
U_TRACE(0, "USocketExt::vsyncCommand(%p,%p,%u,%.*S,%u)", sk, buffer, buffer_size, fmt_size, format, fmt_size)
|
|
|
|
U_INTERNAL_ASSERT(sk->isOpen())
|
|
|
|
uint32_t buffer_len = u__vsnprintf(buffer, buffer_size-2, format, fmt_size, argp);
|
|
|
|
buffer[buffer_len++] = '\r';
|
|
buffer[buffer_len++] = '\n';
|
|
|
|
int n = sk->send(buffer, buffer_len),
|
|
response = (sk->checkIO(n) ? readLineReply(sk, buffer, buffer_size) : 0);
|
|
|
|
U_RETURN(response);
|
|
}
|
|
|
|
// Send a command to a server and wait for a response (multi line)
|
|
|
|
int USocketExt::vsyncCommandML(USocket* sk, char* buffer, uint32_t buffer_size, const char* format, uint32_t fmt_size, va_list argp)
|
|
{
|
|
U_TRACE(0, "USocketExt::vsyncCommandML(%p,%p,%u,%.*S,%u)", sk, buffer, buffer_size, fmt_size, format, fmt_size)
|
|
|
|
U_INTERNAL_ASSERT(sk->isOpen())
|
|
|
|
uint32_t buffer_len = u__vsnprintf(buffer, buffer_size-2, format, fmt_size, argp);
|
|
|
|
buffer[buffer_len++] = '\r';
|
|
buffer[buffer_len++] = '\n';
|
|
|
|
int n = sk->send(buffer, buffer_len),
|
|
response = (sk->checkIO(n) ? readMultilineReply(sk, buffer, buffer_size) : 0);
|
|
|
|
U_RETURN(response);
|
|
}
|
|
|
|
// Send a command to a server and wait for a response (check for token line)
|
|
|
|
int USocketExt::vsyncCommandToken(USocket* sk, UString& buffer, const char* format, uint32_t fmt_size, va_list argp)
|
|
{
|
|
U_TRACE(1, "USocketExt::vsyncCommandToken(%p,%V,%.*S,%u)", sk, buffer.rep, fmt_size, format, fmt_size)
|
|
|
|
U_INTERNAL_ASSERT(sk->isOpen())
|
|
U_INTERNAL_ASSERT_EQUALS((bool)buffer, false)
|
|
|
|
static uint32_t cmd_count;
|
|
|
|
char token[32];
|
|
uint32_t token_len = u__snprintf(token, sizeof(token), U_CONSTANT_TO_PARAM("U%04u "), cmd_count++);
|
|
|
|
U_INTERNAL_DUMP("token = %.*S", token_len, token)
|
|
|
|
char* p = buffer.data();
|
|
|
|
U_MEMCPY(p, token, token_len);
|
|
|
|
uint32_t buffer_len = token_len + u__vsnprintf(p+token_len, buffer.capacity(), format, fmt_size, argp);
|
|
|
|
p[buffer_len++] = '\r';
|
|
p[buffer_len++] = '\n';
|
|
|
|
int n = sk->send(p, buffer_len);
|
|
|
|
if (sk->checkIO(n))
|
|
{
|
|
uint32_t pos_token = USocketExt::readWhileNotToken(sk, buffer, token, token_len);
|
|
|
|
if (pos_token != U_NOT_FOUND)
|
|
{
|
|
U_ASSERT(buffer.c_char(buffer.size()-1) == '\n')
|
|
# ifdef DEBUG
|
|
if (pos_token) { U_ASSERT(buffer.c_char(pos_token-1) == '\n') }
|
|
# endif
|
|
|
|
U_RETURN(pos_token + token_len);
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
U_NO_EXPORT inline bool USocketExt::parseCommandResponse(char* buffer, int r, int response)
|
|
{
|
|
U_TRACE(0, "USocketExt::parseCommandResponse(%p,%d,%d)", buffer, r, response)
|
|
|
|
/**
|
|
* Thus the format for multi-line replies is that the first line will begin with the exact required reply code,
|
|
* followed immediately by a Hyphen, "-" (also known as Minus), followed by text. The last line will begin with
|
|
* the same code, followed immediately by Space <SP>, optionally some text, and the Telnet end-of-line code.
|
|
* For example:
|
|
* 123-First line
|
|
* Second line
|
|
* 234 A line beginning with numbers
|
|
* 123 The last line
|
|
* The user-process then simply needs to search for the second occurrence of the same reply code, followed by
|
|
* <SP> (Space), at the beginning of a line, and ignore all intermediary lines. If an intermediary line begins
|
|
* with a 3-digit number, the Server must pad the front to avoid confusion
|
|
*/
|
|
|
|
int complete = 2;
|
|
|
|
if (buffer[3] == '-')
|
|
{
|
|
complete = 0;
|
|
|
|
for (int i = 0; i < r; ++i)
|
|
{
|
|
if (buffer[i] == '\n')
|
|
{
|
|
if (complete == 1)
|
|
{
|
|
complete = 2;
|
|
|
|
break;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("buffer = %S", buffer+i+1)
|
|
|
|
if (buffer[i+4] == ' ')
|
|
{
|
|
int j = -1;
|
|
|
|
(void) sscanf(buffer+i+1, "%3i", &j);
|
|
|
|
U_INTERNAL_DUMP("j = %d response = %d", j, response)
|
|
|
|
if (j == response) complete = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_DUMP("complete = %d", complete)
|
|
|
|
U_RETURN(complete != 2);
|
|
}
|
|
|
|
int USocketExt::readLineReply(USocket* sk, char* buffer, uint32_t buffer_size) // response from server (single line)
|
|
{
|
|
U_TRACE(0, "USocketExt::readLineReply(%p,%p,%u)", sk, buffer, buffer_size)
|
|
|
|
U_INTERNAL_ASSERT(sk->isConnected())
|
|
|
|
int i, r = 0;
|
|
|
|
do {
|
|
int count = buffer_size - r;
|
|
|
|
i = sk->recv(buffer + r, count);
|
|
|
|
if (sk->checkIO(i) == false) U_RETURN(0);
|
|
|
|
r += i;
|
|
}
|
|
while (buffer[r-1] != '\n');
|
|
|
|
buffer[r] = '\0';
|
|
|
|
U_RETURN(r);
|
|
}
|
|
|
|
int USocketExt::readMultilineReply(USocket* sk, char* buffer, uint32_t buffer_size) // response from server (multi line)
|
|
{
|
|
U_TRACE(0, "USocketExt::readMultilineReply(%p,%p,%u)", sk, buffer, buffer_size)
|
|
|
|
U_INTERNAL_ASSERT(sk->isConnected())
|
|
|
|
int r = 0, response = 0;
|
|
|
|
do {
|
|
r = readLineReply(sk, buffer + r, buffer_size - r);
|
|
|
|
if (r) response = u_atoi(buffer);
|
|
}
|
|
while (parseCommandResponse(buffer, r, response));
|
|
|
|
U_RETURN(response);
|
|
}
|
|
|
|
// SERVICES
|
|
|
|
UString USocketExt::getNetworkDevice(const char* exclude)
|
|
{
|
|
U_TRACE(1, "USocketExt::getNetworkDevice(%S)", exclude)
|
|
|
|
UString result(100U);
|
|
|
|
#if !defined(_MSWINDOWS_) && defined(HAVE_SYS_IOCTL_H)
|
|
FILE* route = (FILE*) U_SYSCALL(fopen, "%S,%S", "/proc/net/route", "r");
|
|
|
|
if (U_SYSCALL(fscanf, "%p,%S", route, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") != EOF) // Skip the first line
|
|
{
|
|
char dev[7],
|
|
dest[9];
|
|
char* ptr = dest;
|
|
|
|
while (U_SYSCALL(fscanf, "%p,%S", route, "%6s %8s %*s %*s %*s %*s %*s %*s %*s %*s %*s\n", dev, dest) != EOF)
|
|
{
|
|
bool found = (exclude ? (strncmp(dev, exclude, 6) != 0) // not the whatever it is
|
|
: (u_get_unalignedp64(ptr) == U_MULTICHAR_CONSTANT64('0','0','0','0','0','0','0','0'))); // default route
|
|
|
|
if (found)
|
|
{
|
|
(void) result.assign(dev);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
(void) U_SYSCALL(fclose, "%p", route);
|
|
#endif
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|
|
|
|
bool USocketExt::getARPCache(UString& cache, UVector<UString>& vec)
|
|
{
|
|
U_TRACE(0+256, "USocketExt::getARPCache(%V,%p)", cache.rep, &vec)
|
|
|
|
#if !defined(_MSWINDOWS_) && defined(HAVE_SYS_IOCTL_H)
|
|
/*
|
|
FILE* arp = (FILE*) U_SYSCALL(fopen, "%S,%S", "/proc/net/arp", "r");
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Skip the first line
|
|
// ------------------------------------------------------------------------------
|
|
// IP address HW type Flags HW address Mask Device
|
|
// 192.168.253.1 0x1 0x2 00:14:a5:6e:9c:cb * ath0
|
|
// 10.30.1.131 0x1 0x2 00:16:ec:fb:46:da * eth0
|
|
// ------------------------------------------------------------------------------
|
|
|
|
if (U_SYSCALL(fscanf, "%p,%S", arp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s") != EOF)
|
|
{
|
|
char _ip[16];
|
|
|
|
while (U_SYSCALL(fscanf, "%p,%S", arp, "%15s %*s %*s %*s %*s %*s\n", _ip) != EOF)
|
|
{
|
|
UString item((void*)_ip);
|
|
|
|
vec.push_back(item);
|
|
}
|
|
}
|
|
|
|
(void) U_SYSCALL(fclose, "%p", arp);
|
|
*/
|
|
|
|
UString content = UFile::getSysContent("/proc/net/arp");
|
|
|
|
if (cache != content)
|
|
{
|
|
UString item;
|
|
UVector<UString> vec_row(content, '\n'), vec_entry(6);
|
|
|
|
vec.clear();
|
|
|
|
cache = content;
|
|
|
|
for (uint32_t i = 1, n = vec_row.size(); i < n; ++i) // Skip the first line
|
|
{
|
|
// ------------------------------------------------------------------------------
|
|
// IP address HW type Flags HW address Mask Device
|
|
// ------------------------------------------------------------------------------
|
|
// 192.168.253.1 0x1 0x2 00:14:a5:6e:9c:cb * ath0
|
|
// 10.30.1.131 0x1 0x2 00:16:ec:fb:46:da * eth0
|
|
// ------------------------------------------------------------------------------
|
|
|
|
(void) vec_entry.split(vec_row[i]);
|
|
|
|
item = vec_entry[0]; // ip
|
|
|
|
U_INTERNAL_ASSERT(item)
|
|
|
|
vec.push_back(item);
|
|
|
|
item = vec_entry[3]; // mac
|
|
|
|
U_INTERNAL_ASSERT(item)
|
|
|
|
vec.push_back(item);
|
|
|
|
item = vec_entry[5]; // dev
|
|
|
|
U_INTERNAL_ASSERT(item)
|
|
|
|
vec.push_back(item);
|
|
|
|
vec_entry.clear();
|
|
}
|
|
|
|
U_RETURN(true);
|
|
}
|
|
#endif
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
UString USocketExt::getNetworkInterfaceName(const char* ip, uint32_t ip_len)
|
|
{
|
|
U_TRACE(0, "USocketExt::getNetworkInterfaceName(%.*S,%u)", ip_len, ip, ip_len)
|
|
|
|
U_INTERNAL_ASSERT(u_isIPv4Addr(ip, ip_len))
|
|
|
|
UString result(100U);
|
|
|
|
#if !defined(_MSWINDOWS_) && defined(HAVE_SYS_IOCTL_H)
|
|
/*
|
|
FILE* arp = (FILE*) U_SYSCALL(fopen, "%S,%S", "/proc/net/arp", "r");
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Skip the first line
|
|
// ------------------------------------------------------------------------------
|
|
// IP address HW type Flags HW address Mask Device
|
|
// 192.168.253.1 0x1 0x2 00:14:a5:6e:9c:cb * ath0
|
|
// 10.30.1.131 0x1 0x2 00:16:ec:fb:46:da * eth0
|
|
// ------------------------------------------------------------------------------
|
|
|
|
if (U_SYSCALL(fscanf, "%p,%S", arp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s") != EOF)
|
|
{
|
|
char _ip[16], dev[16];
|
|
|
|
while (U_SYSCALL(fscanf, "%p,%S", arp, "%15s %*s %*s %*s %*s %15s\n", _ip, dev) != EOF)
|
|
{
|
|
if (strcmp(ip, _ip) == 0)
|
|
{
|
|
(void) result.assign(dev);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
(void) U_SYSCALL(fclose, "%p", arp);
|
|
*/
|
|
|
|
UString content;
|
|
UVector<UString> vec;
|
|
|
|
if (getARPCache(content, vec))
|
|
{
|
|
for (uint32_t i = 0, n = vec.size(); i < n; i += 3)
|
|
{
|
|
if (vec[i].equal(ip, ip_len))
|
|
{
|
|
result = vec[i+2].copy();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|
|
|
|
UString USocketExt::getMacAddress(const char* ip, uint32_t ip_len)
|
|
{
|
|
U_TRACE(0, "USocketExt::getMacAddress(%.*S,%u)", ip_len, ip, ip_len)
|
|
|
|
U_INTERNAL_ASSERT(u_isIPv4Addr(ip, ip_len))
|
|
|
|
#if !defined(_MSWINDOWS_) && defined(HAVE_SYS_IOCTL_H)
|
|
/*
|
|
FILE* arp = (FILE*) U_SYSCALL(fopen, "%S,%S", "/proc/net/arp", "r");
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Skip the first line
|
|
// ------------------------------------------------------------------------------
|
|
// IP address HW type Flags HW address Mask Device
|
|
// 192.168.253.1 0x1 0x2 00:14:a5:6e:9c:cb * ath0
|
|
// 10.30.1.131 0x1 0x2 00:16:ec:fb:46:da * eth0
|
|
// ------------------------------------------------------------------------------
|
|
|
|
if (U_SYSCALL(fscanf, "%p,%S", arp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s") != EOF)
|
|
{
|
|
char ip[16], hw[18];
|
|
|
|
while (U_SYSCALL(fscanf, "%p,%S", arp, "%15s %*s %*s %17s %*s %*s\n", ip, hw) != EOF)
|
|
{
|
|
if (strncmp(device_or_ip, ip, sizeof(ip)) == 0)
|
|
{
|
|
(void) result.assign(hw);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
(void) U_SYSCALL(fclose, "%p", arp);
|
|
*/
|
|
|
|
UString content;
|
|
UVector<UString> vec;
|
|
|
|
if (getARPCache(content, vec))
|
|
{
|
|
UString result;
|
|
|
|
for (uint32_t i = 0, n = vec.size(); i < n; i += 3)
|
|
{
|
|
if (vec[i].equal(ip, ip_len))
|
|
{
|
|
result = vec[i+1].copy();
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
U_RETURN_STRING(*UString::str_without_mac);
|
|
}
|
|
|
|
UString USocketExt::getMacAddress(int fd, const char* device)
|
|
{
|
|
U_TRACE(1, "USocketExt::getMacAddress(%d,%S)", fd, device)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(device)
|
|
|
|
UString result(100U);
|
|
|
|
#ifdef U_LINUX
|
|
U_INTERNAL_ASSERT(fd != -1)
|
|
|
|
struct ifreq ifr;
|
|
|
|
(void) u__strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
|
|
|
|
if (U_SYSCALL(ioctl, "%d,%d,%p", fd, SIOCGIFHWADDR, &ifr) == 0)
|
|
{
|
|
char* hwaddr = ifr.ifr_hwaddr.sa_data;
|
|
|
|
result.snprintf(U_CONSTANT_TO_PARAM("%02x:%02x:%02x:%02x:%02x:%02x"),
|
|
hwaddr[0] & 0xFF,
|
|
hwaddr[1] & 0xFF,
|
|
hwaddr[2] & 0xFF,
|
|
hwaddr[3] & 0xFF,
|
|
hwaddr[4] & 0xFF,
|
|
hwaddr[5] & 0xFF);
|
|
|
|
U_INTERNAL_ASSERT(u_isMacAddr(U_STRING_TO_PARAM(result)))
|
|
}
|
|
#endif
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|
|
|
|
UString USocketExt::getMacAddress(USocket* socket, const char* device)
|
|
{
|
|
U_TRACE(1, "USocketExt::getMacAddress(%p,%S)", socket, device)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(device)
|
|
|
|
UString result(100U);
|
|
|
|
#if defined(U_LINUX) && defined(HAVE_SYS_IOCTL_H) && defined(HAVE_ARPA_INET_H)
|
|
U_INTERNAL_ASSERT(socket->isOpen())
|
|
|
|
/**
|
|
* ARP ioctl request
|
|
*
|
|
* struct arpreq {
|
|
* struct sockaddr arp_pa; // Protocol address
|
|
* struct sockaddr arp_ha; // Hardware address
|
|
* int arp_flags; // Flags
|
|
* struct sockaddr arp_netmask; // Netmask (only for proxy arps)
|
|
* char arp_dev[16];
|
|
* };
|
|
*/
|
|
|
|
struct arpreq arpreq;
|
|
|
|
(void) U_SYSCALL(memset, "%p,%d,%u", &arpreq, 0, sizeof(arpreq));
|
|
|
|
union uupsockaddr {
|
|
struct sockaddr* p;
|
|
struct sockaddr_in* psin;
|
|
};
|
|
|
|
union uupsockaddr u = { &arpreq.arp_pa };
|
|
|
|
// arp_pa must be an AF_INET address
|
|
// arp_ha must have the same type as the device which is specified in arp_dev
|
|
// arp_dev is a zero-terminated string which names a device
|
|
|
|
u.psin->sin_family = AF_INET;
|
|
u.psin->sin_addr.s_addr = socket->getClientAddress();
|
|
arpreq.arp_ha.sa_family = AF_INET;
|
|
|
|
(void) u__strncpy(arpreq.arp_dev, device, 15);
|
|
|
|
if (U_SYSCALL(ioctl, "%d,%d,%p", socket->iSockDesc, SIOCGARP, &arpreq) == 0)
|
|
{
|
|
if ((arpreq.arp_flags & ATF_COM) != 0)
|
|
{
|
|
unsigned char* hwaddr = (unsigned char*)arpreq.arp_ha.sa_data;
|
|
|
|
result.snprintf(U_CONSTANT_TO_PARAM("%02x:%02x:%02x:%02x:%02x:%02x"),
|
|
hwaddr[0] & 0xFF,
|
|
hwaddr[1] & 0xFF,
|
|
hwaddr[2] & 0xFF,
|
|
hwaddr[3] & 0xFF,
|
|
hwaddr[4] & 0xFF,
|
|
hwaddr[5] & 0xFF);
|
|
|
|
U_INTERNAL_ASSERT(u_isMacAddr(U_STRING_TO_PARAM(result)))
|
|
|
|
/**
|
|
* if (arpreq.arp_flags & ATF_PERM) printf("PERM");
|
|
* if (arpreq.arp_flags & ATF_PUBL) printf("PUBLISHED");
|
|
* if (arpreq.arp_flags & ATF_USETRAILERS) printf("TRAILERS");
|
|
* if (arpreq.arp_flags & ATF_PROXY) printf("PROXY");
|
|
*/
|
|
}
|
|
// else printf("*** INCOMPLETE ***");
|
|
}
|
|
#endif
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|
|
|
|
UString USocketExt::getIPAddress(int fd, const char* device)
|
|
{
|
|
U_TRACE(1, "USocketExt::getIPAddress(%d,%S)", fd, device)
|
|
|
|
U_INTERNAL_ASSERT(fd != -1)
|
|
U_INTERNAL_ASSERT_POINTER(device)
|
|
|
|
UString result(100U);
|
|
|
|
#ifdef U_LINUX
|
|
struct ifreq ifr;
|
|
|
|
(void) u__strncpy(ifr.ifr_name, device, IFNAMSIZ-1);
|
|
|
|
/* Get the IP address of the interface */
|
|
|
|
if (U_SYSCALL(ioctl, "%d,%d,%p", fd, SIOCGIFADDR, &ifr) == 0)
|
|
{
|
|
uusockaddr addr;
|
|
|
|
U_MEMCPY(&addr, &ifr.ifr_addr, sizeof(struct sockaddr));
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(addr.psaIP4Addr.sin_family, AF_INET)
|
|
|
|
(void) U_SYSCALL(inet_ntop, "%d,%p,%p,%u", AF_INET, &(addr.psaIP4Addr.sin_addr), result.data(), INET_ADDRSTRLEN);
|
|
|
|
result.size_adjust();
|
|
}
|
|
#endif
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|
|
|
|
UString USocketExt::getNetworkAddress(int fd, const char* device)
|
|
{
|
|
U_TRACE(1, "USocketExt::getNetworkAddress(%d,%S)", fd, device)
|
|
|
|
U_INTERNAL_ASSERT(fd != -1)
|
|
U_INTERNAL_ASSERT_POINTER(device)
|
|
|
|
UString result(100U);
|
|
|
|
#ifdef U_LINUX
|
|
struct ifreq ifaddr, ifnetmask;
|
|
|
|
(void) u__strncpy( ifaddr.ifr_name, device, IFNAMSIZ-1);
|
|
(void) u__strncpy(ifnetmask.ifr_name, device, IFNAMSIZ-1);
|
|
|
|
// retrieve the IP address and subnet mask
|
|
|
|
if (U_SYSCALL(ioctl, "%d,%d,%p", fd, SIOCGIFADDR, &ifaddr) == 0 &&
|
|
U_SYSCALL(ioctl, "%d,%d,%p", fd, SIOCGIFNETMASK, &ifnetmask) == 0)
|
|
{
|
|
// compute the current network value from the address and netmask
|
|
|
|
int network;
|
|
uusockaddr addr, netmask;
|
|
|
|
U_MEMCPY(&addr, &ifaddr.ifr_addr, sizeof(struct sockaddr));
|
|
U_MEMCPY(&netmask, &ifnetmask.ifr_netmask, sizeof(struct sockaddr));
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(addr.psaIP4Addr.sin_family, AF_INET)
|
|
U_INTERNAL_ASSERT_EQUALS(netmask.psaIP4Addr.sin_family, AF_INET)
|
|
|
|
network = addr.psaIP4Addr.sin_addr.s_addr &
|
|
netmask.psaIP4Addr.sin_addr.s_addr;
|
|
|
|
/*
|
|
result.snprintf(U_CONSTANT_TO_PARAM("%d.%d.%d.%d"),
|
|
(network & 0xFF),
|
|
(network >> 8 & 0xFF),
|
|
(network >> 16 & 0xFF),
|
|
(network >> 24 & 0xFF));
|
|
*/
|
|
|
|
(void) U_SYSCALL(inet_ntop, "%d,%p,%p,%u", AF_INET, &network, result.data(), INET_ADDRSTRLEN);
|
|
|
|
result.size_adjust();
|
|
}
|
|
#endif
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|
|
|
|
#ifdef USE_C_ARES
|
|
int USocketExt::resolv_status;
|
|
char USocketExt::resolv_hostname[INET6_ADDRSTRLEN];
|
|
void* USocketExt::resolv_channel;
|
|
|
|
U_NO_EXPORT void USocketExt::callbackResolv(void* arg, int status, int timeouts, struct hostent* phost)
|
|
{
|
|
U_TRACE(0, "USocketExt::callbackResolv(%p,%d,%d,%p)", arg, status, timeouts, phost)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(resolv_channel)
|
|
U_INTERNAL_ASSERT_EQUALS(resolv_status, ARES_ENODATA)
|
|
|
|
resolv_status = status;
|
|
|
|
if (phost)
|
|
{
|
|
# ifdef HAVE_INET_NTOP
|
|
(void) U_SYSCALL(inet_ntop, "%d,%p,%p,%u", phost->h_addrtype, phost->h_addr_list[0], resolv_hostname, INET6_ADDRSTRLEN);
|
|
# else
|
|
char* result = U_SYSCALL(inet_ntoa, "%u", *((struct in_addr*)phost->h_addr_list[0]));
|
|
|
|
if (result) u__strcpy(resolv_hostname, result);
|
|
# endif
|
|
|
|
U_INTERNAL_DUMP("Found address name %s (%s) - status %d timeouts %d", phost->h_name, resolv_hostname, status, timeouts)
|
|
}
|
|
}
|
|
|
|
void USocketExt::waitResolv()
|
|
{
|
|
U_TRACE_NO_PARAM(1, "USocketExt::waitResolv()")
|
|
|
|
U_INTERNAL_ASSERT_POINTER(resolv_channel)
|
|
|
|
while (resolv_status == ARES_ENODATA)
|
|
{
|
|
int nfds;
|
|
struct timeval tv;
|
|
struct timeval* tvp;
|
|
fd_set read_fds, write_fds;
|
|
|
|
FD_ZERO( &read_fds);
|
|
FD_ZERO(&write_fds);
|
|
|
|
nfds = U_SYSCALL(ares_fds, "%p,%p,%p", (ares_channel)resolv_channel, &read_fds, &write_fds);
|
|
|
|
if (nfds <= 0) break;
|
|
|
|
tvp = (struct timeval*) U_SYSCALL(ares_timeout, "%p,%p,%p", (ares_channel)resolv_channel, U_NULLPTR, &tv);
|
|
|
|
(void) U_SYSCALL(select, "%d,%p,%p,%p,%p", nfds, &read_fds, &write_fds, U_NULLPTR, tvp);
|
|
|
|
U_SYSCALL_VOID(ares_process, "%p,%p,%p", (ares_channel)resolv_channel, &read_fds, &write_fds);
|
|
|
|
U_INTERNAL_DUMP("status %d", resolv_status)
|
|
}
|
|
}
|
|
|
|
void USocketExt::startResolv(const char* name, int family)
|
|
{
|
|
U_TRACE(1, "USocketExt::startResolv(%S,%d)", name, family)
|
|
|
|
if (resolv_channel == U_NULLPTR)
|
|
{
|
|
int status = U_SYSCALL(ares_library_init, "%d", ARES_LIB_INIT_ALL);
|
|
|
|
if (status != ARES_SUCCESS) U_ERROR("ares_library_init() failed: %s", ares_strerror(status));
|
|
|
|
struct ares_options options;
|
|
|
|
union uuares_channeldata {
|
|
void** p1;
|
|
ares_channeldata** p2;
|
|
};
|
|
|
|
union uuares_channeldata p = { &resolv_channel };
|
|
|
|
status = U_SYSCALL(ares_init_options, "%p,%p,%d", p.p2, &options, 0);
|
|
|
|
if (status != ARES_SUCCESS) U_ERROR("ares_init_options() failed: %s", ares_strerror(status));
|
|
}
|
|
|
|
resolv_status = ARES_ENODATA;
|
|
|
|
U_SYSCALL_VOID(ares_gethostbyname, "%p,%S,%d,%p,%p", (ares_channel)resolv_channel, name, family, &USocketExt::callbackResolv, U_NULLPTR);
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_LINUX
|
|
# include <linux/types.h>
|
|
# include <linux/rtnetlink.h>
|
|
#endif
|
|
|
|
UString USocketExt::getGatewayAddress(const char* network, uint32_t network_len)
|
|
{
|
|
U_TRACE(1, "USocketExt::getGatewayAddress(%.*S,%u)", network_len, network, network_len)
|
|
|
|
UString result(100U);
|
|
|
|
// Ex: ip route show to exact 192.168.1.0/24
|
|
|
|
#ifdef U_LINUX
|
|
static int sock;
|
|
|
|
if (sock == 0) sock = USocket::socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
|
|
|
if (sock != -1)
|
|
{
|
|
char msgBuf[4096];
|
|
|
|
(void) U_SYSCALL(memset, "%p,%d,%u", msgBuf, 0, 4096);
|
|
|
|
/*
|
|
struct nlmsghdr {
|
|
__u32 nlmsg_len; // Length of message including header
|
|
__u16 nlmsg_type; // Type of message content
|
|
__u16 nlmsg_flags; // Additional flags
|
|
__u32 nlmsg_seq; // Sequence number
|
|
__u32 nlmsg_pid; // PID of the sending process
|
|
};
|
|
*/
|
|
|
|
// point the header and the msg structure pointers into the buffer
|
|
|
|
union uunlmsghdr {
|
|
char* p;
|
|
struct nlmsghdr* h;
|
|
};
|
|
|
|
union uunlmsghdr nlMsg = { &msgBuf[0] };
|
|
|
|
// Fill in the nlmsg header
|
|
|
|
nlMsg.h->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message (28)
|
|
nlMsg.h->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table
|
|
nlMsg.h->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump
|
|
nlMsg.h->nlmsg_seq = 0; // Sequence of the message packet
|
|
nlMsg.h->nlmsg_pid = u_pid; // PID of process sending the request
|
|
|
|
// Send the request
|
|
|
|
if (U_SYSCALL(send, "%d,%p,%u,%u", sock, CAST(nlMsg.h), nlMsg.h->nlmsg_len, 0) == (ssize_t)nlMsg.h->nlmsg_len)
|
|
{
|
|
// Read the response
|
|
|
|
int readLen;
|
|
uint32_t msgLen = 0;
|
|
char* bufPtr = msgBuf;
|
|
union uunlmsghdr nlHdr;
|
|
|
|
do {
|
|
// Receive response from the kernel
|
|
|
|
readLen = U_SYSCALL(recv, "%d,%p,%u,%d", sock, CAST(bufPtr), 4096 - msgLen, 0);
|
|
|
|
if (readLen < 0) break;
|
|
|
|
nlHdr.p = bufPtr;
|
|
|
|
// Check if the header is valid
|
|
|
|
if ((NLMSG_OK(nlHdr.h, (uint32_t)readLen) == 0) || (nlHdr.h->nlmsg_type == NLMSG_ERROR)) break;
|
|
|
|
// Check if it is the last message
|
|
|
|
U_INTERNAL_DUMP("nlmsg_type = %u nlmsg_seq = %u nlmsg_pid = %u nlmsg_flags = %B",
|
|
nlHdr.h->nlmsg_type, nlHdr.h->nlmsg_seq, nlHdr.h->nlmsg_pid, nlHdr.h->nlmsg_flags)
|
|
|
|
if (nlHdr.h->nlmsg_type == NLMSG_DONE) break;
|
|
else
|
|
{
|
|
// Else move the pointer to buffer appropriately
|
|
|
|
bufPtr += readLen;
|
|
msgLen += readLen;
|
|
}
|
|
|
|
// Check if it is a multi part message
|
|
|
|
if ((nlHdr.h->nlmsg_flags & NLM_F_MULTI) == 0) break;
|
|
}
|
|
while ((nlHdr.h->nlmsg_seq != 1) || (nlHdr.h->nlmsg_pid != (uint32_t)u_pid));
|
|
|
|
U_INTERNAL_DUMP("msgLen = %u readLen = %d", msgLen, readLen)
|
|
|
|
// Parse the response
|
|
|
|
int rtLen;
|
|
char* dst;
|
|
char dstMask[32];
|
|
struct rtattr* rtAttr;
|
|
char ifName[IF_NAMESIZE];
|
|
struct in_addr dstAddr, srcAddr, gateWay;
|
|
|
|
for (; NLMSG_OK(nlMsg.h,msgLen); nlMsg.h = NLMSG_NEXT(nlMsg.h,msgLen))
|
|
{
|
|
struct rtmsg* rtMsg = (struct rtmsg*) NLMSG_DATA(nlMsg.h);
|
|
|
|
U_INTERNAL_DUMP("rtMsg = %p msgLen = %u rtm_family = %u rtm_table = %u", rtMsg, msgLen, rtMsg->rtm_family, rtMsg->rtm_table)
|
|
|
|
/*
|
|
#define AF_INET 2 // IP protocol family
|
|
#define AF_INET6 10 // IP version 6
|
|
*/
|
|
|
|
if ((rtMsg->rtm_family != AF_INET)) continue; // If the route is not for AF_INET then continue
|
|
|
|
/* Reserved table identifiers
|
|
|
|
enum rt_class_t {
|
|
RT_TABLE_UNSPEC=0,
|
|
RT_TABLE_COMPAT=252,
|
|
RT_TABLE_DEFAULT=253,
|
|
RT_TABLE_MAIN=254,
|
|
RT_TABLE_LOCAL=255,
|
|
RT_TABLE_MAX=0xFFFFFFFF }; */
|
|
|
|
if ((rtMsg->rtm_table != RT_TABLE_MAIN)) continue; // If the route does not belong to main routing table then continue
|
|
|
|
ifName[0] = '\0';
|
|
dstAddr.s_addr = srcAddr.s_addr = gateWay.s_addr = 0;
|
|
|
|
// get the rtattr field
|
|
|
|
rtAttr = (struct rtattr*) RTM_RTA(rtMsg);
|
|
rtLen = RTM_PAYLOAD(nlMsg.h);
|
|
|
|
for (; RTA_OK(rtAttr,rtLen); rtAttr = RTA_NEXT(rtAttr,rtLen))
|
|
{
|
|
U_INTERNAL_DUMP("rtAttr = %p rtLen = %u rta_type = %u rta_len = %u", rtAttr, rtLen, rtAttr->rta_type, rtAttr->rta_len)
|
|
|
|
/* Routing message attributes
|
|
|
|
struct rtattr {
|
|
unsigned short rta_len; // Length of option
|
|
unsigned short rta_type; // Type of option
|
|
// Data follows
|
|
};
|
|
|
|
enum rtattr_type_t {
|
|
RTA_UNSPEC, // 0
|
|
RTA_DST, // 1
|
|
RTA_SRC, // 2
|
|
RTA_IIF, // 3
|
|
RTA_OIF, // 4
|
|
RTA_GATEWAY, // 5
|
|
RTA_PRIORITY, // 6
|
|
RTA_PREFSRC, // 7
|
|
RTA_METRICS, // 8
|
|
RTA_MULTIPATH, // 9
|
|
RTA_PROTOINFO, // no longer used
|
|
RTA_FLOW, // 11
|
|
RTA_CACHEINFO, // 12
|
|
RTA_SESSION, // no longer used
|
|
RTA_MP_ALGO, // no longer used
|
|
RTA_TABLE, // 15
|
|
RTA_MARK, // 16
|
|
__RTA_MAX }; */
|
|
|
|
switch (rtAttr->rta_type)
|
|
{
|
|
case RTA_OIF: (void) if_indextoname(*(unsigned*)RTA_DATA(rtAttr), ifName); break;
|
|
case RTA_GATEWAY: U_MEMCPY(&gateWay, RTA_DATA(rtAttr), sizeof(struct in_addr)); break;
|
|
case RTA_PREFSRC: U_MEMCPY(&srcAddr, RTA_DATA(rtAttr), sizeof(struct in_addr)); break;
|
|
case RTA_DST: U_MEMCPY(&dstAddr, RTA_DATA(rtAttr), sizeof(struct in_addr)); break;
|
|
}
|
|
}
|
|
|
|
U_DUMP("ifName = %S dstAddr = %S rtMsg->rtm_dst_len = %u srcAddr = %S gateWay = %S", ifName,
|
|
UIPAddress::toString(dstAddr.s_addr).data(), rtMsg->rtm_dst_len,
|
|
UIPAddress::toString(srcAddr.s_addr).data(),
|
|
UIPAddress::toString(gateWay.s_addr).data())
|
|
|
|
dst = U_SYSCALL(inet_ntoa, "%u", dstAddr);
|
|
|
|
if (u__snprintf(dstMask, sizeof(dstMask), U_CONSTANT_TO_PARAM("%s/%u"), dst, rtMsg->rtm_dst_len) == network_len &&
|
|
strncmp(dstMask, network, network_len) == 0)
|
|
{
|
|
if (gateWay.s_addr)
|
|
{
|
|
(void) U_SYSCALL(inet_ntop, "%d,%p,%p,%u", AF_INET, &gateWay, result.data(), result.capacity());
|
|
}
|
|
else
|
|
{
|
|
U_INTERNAL_ASSERT_MAJOR(srcAddr.s_addr, 0)
|
|
|
|
(void) U_SYSCALL(inet_ntop, "%d,%p,%p,%u", AF_INET, &srcAddr, result.data(), result.capacity());
|
|
}
|
|
|
|
result.size_adjust();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
U_RETURN_STRING(result);
|
|
}
|