// Copyright (c) 2002 David Muse
// See the COPYING file for more information
//#define DEBUG_PASSFD 1
//#define DEBUG_WRITE 1
//#define DEBUG_READ 1
#include <rudiments/filedescriptor.h>
#include <rudiments/listener.h>
#include <rudiments/charstring.h>
#include <rudiments/character.h>
#include <rudiments/rawbuffer.h>
#if defined(DEBUG_PASSFD) || defined(DEBUG_WRITE) || defined(DEBUG_READ)
#include <rudiments/process.h>
#endif
#include <rudiments/error.h>
#ifdef RUDIMENTS_HAVE_WINDOWS_H
#include <windows.h>
#endif
#ifdef RUDIMENTS_HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef RUDIMENTS_HAVE_IO_H
#include <io.h>
#endif
#include <stdio.h>
#include <sys/time.h>
#ifdef RUDIMENTS_HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef RUDIMENTS_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#ifdef HAVE_RUDIMENTS_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
#ifdef RUDIMENTS_HAVE_IOCTL
#include <sys/ioctl.h>
#endif
#ifdef RUDIMENTS_HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef RUDIMENTS_HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef RUDIMENTS_HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#include <limits.h>
#ifdef RUDIMENTS_HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef RUDIMENTS_HAVE_BYTESWAP_H
#include <byteswap.h>
#endif
#ifdef RUDIMENTS_HAVE_MACHINE_ENDIAN_H
#include <machine/endian.h>
#endif
#ifdef RUDIMENTS_HAVE_OSSWAPHOSTTOLITTLEINT64
#include <libkern/OSByteOrder.h>
#endif
#ifndef __BYTE_ORDER
#define __BYTE_ORDER BYTE_ORDER
#endif
#ifndef __BIG_ENDIAN
#define __BIG_ENDIAN BIG_ENDIAN
#endif
// for FD_SET (macro that uses memset) on solaris
#include <string.h>
#ifdef RUDIMENTS_NEED_XNET_PROTOTYPES
extern ssize_t __xnet_recvmsg (int, struct msghdr *, int);
extern ssize_t __xnet_sendmsg (int, const struct msghdr *, int);
#endif
// if SSIZE_MAX is undefined, choose a good safe value
// that should even work on 16-bit systems
#ifndef SSIZE_MAX
#define SSIZE_MAX 32767
#endif
#ifdef DEBUG_WRITE
#define DEBUG_WRITE_INT(type,number) \
printf("%d: %s write(%d,%d)\n", \
process::getProcessId(),type,pvt->_fd,number);
#define DEBUG_WRITE_FLOAT(type,number) \
printf("%d: %s write(%d,%f)\n", \
process::getProcessId(),type,pvt->_fd,number);
#define DEBUG_WRITE_CHAR(type,character) \
printf("%d: %s write(%d,%d)\n", \
process::getProcessId(),type,pvt->_fd,character);
#define DEBUG_WRITE_STRING(type,string,size) \
printf("%d: %s write(%d,",process::getProcessId(), \
type,pvt->_fd); \
for (size_t i=0; i<((size<=160)?size:160); i++) { \
printf("%c",string[i]); \
} \
if (size>160) { \
printf("..."); \
} \
printf(",%d)\n",size);
#define DEBUG_WRITE_VOID(type,buffer,size) \
const unsigned char *ptr=\
static_cast<const unsigned char *>(buffer); \
printf("%d: %s write(%d,",process::getProcessId(), \
type,pvt->_fd); \
for (size_t i=0; i<((size<=160)?size:160); i++) { \
printf("0x%02x ",ptr[i]); \
} \
if (size>160) { \
printf("..."); \
} \
printf(",%d)\n",size);
#else
#define DEBUG_WRITE_INT(type,number)
#define DEBUG_WRITE_FLOAT(type,number)
#define DEBUG_WRITE_CHAR(type,character)
#define DEBUG_WRITE_STRING(type,string,size)
#define DEBUG_WRITE_VOID(type,buffer,size)
#endif
#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif
class filedescriptorprivate {
friend class filedescriptor;
private:
int _fd;
bool _retryinterruptedreads;
bool _retryinterruptedwrites;
bool _retryinterruptedwaits;
#ifdef RUDIMENTS_HAVE_FCNTL
bool _retryinterruptedfcntl;
#endif
#ifdef RUDIMENTS_HAVE_IOCTL
bool _retryinterruptedioctl;
#endif
bool _allowshortreads;
bool _allowshortwrites;
bool _translatebyteorder;
listener *_lstnr;
bool _uselistenerinsidereads;
bool _uselistenerinsidewrites;
#ifdef RUDIMENTS_HAS_SSL
SSL_CTX *_ctx;
SSL *_ssl;
BIO *_bio;
int _sslresult;
#endif
const char *_type;
unsigned char *_writebuffer;
unsigned char *_writebufferend;
unsigned char *_writebufferptr;
unsigned char *_readbuffer;
unsigned char *_readbufferend;
unsigned char *_readbufferhead;
unsigned char *_readbuffertail;
};
filedescriptor::filedescriptor() {
pvt=new filedescriptorprivate;
filedescriptorInit();
}
filedescriptor::filedescriptor(const filedescriptor &f) {
pvt=new filedescriptorprivate;
filedescriptorClone(f);
}
filedescriptor &filedescriptor::operator=(const filedescriptor &f) {
if (this!=&f) {
delete[] pvt->_writebuffer;
filedescriptorClone(f);
}
return *this;
}
void filedescriptor::filedescriptorInit() {
pvt->_fd=-1;
pvt->_retryinterruptedreads=false;
pvt->_retryinterruptedwrites=false;
pvt->_retryinterruptedwaits=true;
#ifdef RUDIMENTS_HAVE_FCNTL
pvt->_retryinterruptedfcntl=true;
#endif
#ifdef RUDIMENTS_HAVE_IOCTL
pvt->_retryinterruptedioctl=true;
#endif
pvt->_allowshortreads=false;
pvt->_allowshortwrites=false;
pvt->_translatebyteorder=false;
pvt->_lstnr=NULL;
pvt->_uselistenerinsidereads=false;
pvt->_uselistenerinsidewrites=false;
#ifdef RUDIMENTS_HAS_SSL
pvt->_ctx=NULL;
pvt->_bio=NULL;
pvt->_ssl=NULL;
pvt->_sslresult=1;
#endif
pvt->_type="filedescriptor";
pvt->_writebuffer=NULL;
pvt->_writebufferend=NULL;
pvt->_writebufferptr=NULL;
pvt->_readbuffer=NULL;
pvt->_readbufferend=NULL;
pvt->_readbufferhead=NULL;
pvt->_readbuffertail=NULL;
}
void filedescriptor::filedescriptorClone(const filedescriptor &f) {
pvt->_fd=f.pvt->_fd;
pvt->_translatebyteorder=f.pvt->_translatebyteorder;
pvt->_retryinterruptedreads=f.pvt->_retryinterruptedreads;
pvt->_retryinterruptedwrites=f.pvt->_retryinterruptedwrites;
pvt->_retryinterruptedwaits=f.pvt->_retryinterruptedwaits;
#ifdef RUDIMENTS_HAVE_FCNTL
pvt->_retryinterruptedfcntl=f.pvt->_retryinterruptedfcntl;
#endif
#ifdef RUDIMENTS_HAVE_IOCTL
pvt->_retryinterruptedioctl=f.pvt->_retryinterruptedioctl;
#endif
pvt->_allowshortreads=f.pvt->_allowshortreads;
pvt->_allowshortwrites=f.pvt->_allowshortwrites;
pvt->_lstnr=f.pvt->_lstnr;
pvt->_uselistenerinsidereads=f.pvt->_uselistenerinsidereads;
pvt->_uselistenerinsidewrites=f.pvt->_uselistenerinsidewrites;
#ifdef RUDIMENTS_HAS_SSL
pvt->_ctx=f.pvt->_ctx;
pvt->_bio=f.pvt->_bio;
pvt->_ssl=f.pvt->_ssl;
pvt->_sslresult=f.pvt->_sslresult;
#endif
if (f.pvt->_writebuffer) {
ssize_t writebuffersize=f.pvt->_writebufferend-
f.pvt->_writebuffer;
pvt->_writebuffer=new unsigned char[writebuffersize];
rawbuffer::copy(pvt->_writebuffer,
f.pvt->_writebuffer,
writebuffersize);
pvt->_writebufferend=pvt->_writebuffer+writebuffersize;
pvt->_writebufferptr=pvt->_writebuffer+
(f.pvt->_writebufferptr-f.pvt->_writebuffer);
} else {
pvt->_writebuffer=NULL;
pvt->_writebufferend=NULL;
pvt->_writebufferptr=NULL;
}
}
filedescriptor::~filedescriptor() {
delete[] pvt->_readbuffer;
delete[] pvt->_writebuffer;
close();
#ifdef RUDIMENTS_HAS_SSL
setSSLContext(NULL);
#endif
delete pvt;
}
bool filedescriptor::setWriteBufferSize(ssize_t size) const {
if (size<0) {
return false;
}
delete[] pvt->_writebuffer;
pvt->_writebuffer=(size)?new unsigned char[size]:NULL;
pvt->_writebufferend=pvt->_writebuffer+size;
pvt->_writebufferptr=pvt->_writebuffer;
return true;
}
bool filedescriptor::setReadBufferSize(ssize_t size) const {
if (size<0) {
return false;
}
delete[] pvt->_readbuffer;
pvt->_readbuffer=(size)?new unsigned char[size]:NULL;
pvt->_readbufferend=pvt->_readbuffer+size;
pvt->_readbufferhead=pvt->_readbuffer;
pvt->_readbuffertail=pvt->_readbuffer;
return true;
}
int filedescriptor::getFileDescriptor() const {
return pvt->_fd;
}
void filedescriptor::setFileDescriptor(int filedesc) {
pvt->_fd=filedesc;
}
int filedescriptor::duplicate() const {
int result;
do {
result=dup(pvt->_fd);
} while (result==-1 && error::getErrorNumber()==EINTR);
return result;
}
bool filedescriptor::duplicate(int newfd) const {
int result;
do {
result=dup2(pvt->_fd,newfd);
} while (result==-1 && error::getErrorNumber()==EINTR);
return (result==newfd);
}
#ifdef RUDIMENTS_HAS_SSL
void filedescriptor::setSSLContext(SSL_CTX *ctx) {
if (!ctx) {
deInitializeSSL();
}
pvt->_ctx=ctx;
}
SSL_CTX *filedescriptor::getSSLContext() {
return pvt->_ctx;
}
bool filedescriptor::initializeSSL() {
if (pvt->_fd==-1) {
return false;
}
deInitializeSSL();
if (pvt->_ctx) {
pvt->_bio=newSSLBIO();
pvt->_ssl=SSL_new(pvt->_ctx);
SSL_set_bio(pvt->_ssl,pvt->_bio,pvt->_bio);
}
return true;
}
void filedescriptor::deInitializeSSL() {
if (pvt->_ssl) {
SSL_free(pvt->_ssl);
pvt->_ssl=NULL;
}
if (pvt->_bio) {
// BIO_free causes a segfault, and none of the example code
// that I've seen calls it, but the function exists so
// presumably it has a purpose.
//BIO_free(pvt->_bio);
pvt->_bio=NULL;
}
}
SSL *filedescriptor::getSSL() const {
return pvt->_ssl;
}
BIO *filedescriptor::newSSLBIO() const {
return BIO_new_fd(pvt->_fd,BIO_NOCLOSE);
}
#endif
bool filedescriptor::useNonBlockingMode() const {
#if defined(RUDIMENTS_HAVE_FCNTL) && \
defined(F_SETFL) && defined (F_GETFL)
return (fcntl(F_SETFL,fcntl(F_GETFL,0)|O_NONBLOCK)!=-1);
#else
return false;
#endif
}
bool filedescriptor::useBlockingMode() const {
#if defined(RUDIMENTS_HAVE_FCNTL) && \
defined(F_SETFL) && defined (F_GETFL)
return (fcntl(F_SETFL,fcntl(F_GETFL,0)&(~O_NONBLOCK))!=-1);
#else
return false;
#endif
}
bool filedescriptor::isUsingNonBlockingMode() const {
#if defined(RUDIMENTS_HAVE_FCNTL) && defined(F_GETFL)
return (fcntl(F_GETFL,0)&O_NONBLOCK);
#else
return false;
#endif
}
ssize_t filedescriptor::write(uint16_t number) const {
return write(number,-1,-1);
}
ssize_t filedescriptor::write(uint32_t number) const {
return write(number,-1,-1);
}
ssize_t filedescriptor::write(uint64_t number) const {
return write(number,-1,-1);
}
ssize_t filedescriptor::write(int16_t number) const {
DEBUG_WRITE_INT("int16_t",number);
return bufferedWrite(&number,sizeof(int16_t),-1,-1);
}
ssize_t filedescriptor::write(int32_t number) const {
DEBUG_WRITE_INT("int32_t",number);
return bufferedWrite(&number,sizeof(int32_t),-1,-1);
}
ssize_t filedescriptor::write(int64_t number) const {
DEBUG_WRITE_INT("int64_t",number);
return bufferedWrite(&number,sizeof(int64_t),-1,-1);
}
ssize_t filedescriptor::write(float number) const {
DEBUG_WRITE_FLOAT("float",number);
return bufferedWrite(&number,sizeof(float),-1,-1);
}
ssize_t filedescriptor::write(double number) const {
DEBUG_WRITE_FLOAT("double",number);
return bufferedWrite(&number,sizeof(double),-1,-1);
}
ssize_t filedescriptor::write(unsigned char character) const {
DEBUG_WRITE_CHAR("uchar",character);
return bufferedWrite(&character,sizeof(unsigned char),-1,-1);
}
ssize_t filedescriptor::write(bool value) const {
DEBUG_WRITE_INT("bool",value);
return bufferedWrite(&value,sizeof(bool),-1,-1);
}
ssize_t filedescriptor::write(char character) const {
DEBUG_WRITE_CHAR("char",character);
return bufferedWrite(&character,sizeof(char),-1,-1);
}
ssize_t filedescriptor::write(const unsigned char *string, size_t size) const {
DEBUG_WRITE_STRING("ustring",string,size);
return bufferedWrite(string,size,-1,-1);
}
ssize_t filedescriptor::write(const char *string, size_t size) const {
DEBUG_WRITE_STRING("string",string,size);
return bufferedWrite(string,size,-1,-1);
}
ssize_t filedescriptor::write(const unsigned char *string) const {
DEBUG_WRITE_STRING("ustring",string,charstring::length(string));
return bufferedWrite(string,charstring::length(string),-1,-1);
}
ssize_t filedescriptor::write(const char *string) const {
DEBUG_WRITE_STRING("string",string,charstring::length(string));
return bufferedWrite(string,charstring::length(string),-1,-1);
}
ssize_t filedescriptor::write(const void *buffer, size_t size) const {
DEBUG_WRITE_VOID("void",buffer,size);
return bufferedWrite(buffer,size,-1,-1);
}
ssize_t filedescriptor::write(uint16_t number,
long sec, long usec) const {
DEBUG_WRITE_INT("uint16_t",number);
number=hostToNet(number);
return bufferedWrite(&number,sizeof(uint16_t),sec,usec);
}
ssize_t filedescriptor::write(uint32_t number,
long sec, long usec) const {
DEBUG_WRITE_INT("uint32_t",number);
number=hostToNet(number);
return bufferedWrite(&number,sizeof(uint32_t),sec,usec);
}
ssize_t filedescriptor::write(uint64_t number,
long sec, long usec) const {
DEBUG_WRITE_INT("uint64_t",number);
number=hostToNet(number);
return bufferedWrite(&number,sizeof(uint64_t),sec,usec);
}
ssize_t filedescriptor::write(int16_t number,
long sec, long usec) const {
DEBUG_WRITE_INT("int16_t",number);
return bufferedWrite(&number,sizeof(int16_t),sec,usec);
}
ssize_t filedescriptor::write(int32_t number,
long sec, long usec) const {
DEBUG_WRITE_INT("int32_t",number);
return bufferedWrite(&number,sizeof(int32_t),sec,usec);
}
ssize_t filedescriptor::write(int64_t number,
long sec, long usec) const {
DEBUG_WRITE_INT("int64_t",number);
return bufferedWrite(&number,sizeof(int64_t),sec,usec);
}
ssize_t filedescriptor::write(float number,
long sec,long usec) const {
DEBUG_WRITE_FLOAT("float",number);
return bufferedWrite(&number,sizeof(float),sec,usec);
}
ssize_t filedescriptor::write(double number,
long sec, long usec) const {
DEBUG_WRITE_FLOAT("double",number);
return bufferedWrite(&number,sizeof(double),sec,usec);
}
ssize_t filedescriptor::write(unsigned char character,
long sec, long usec) const {
DEBUG_WRITE_CHAR("uchar",character);
return bufferedWrite(&character,sizeof(unsigned char),sec,usec);
}
ssize_t filedescriptor::write(bool value,
long sec, long usec) const {
DEBUG_WRITE_INT("bool",value);
return bufferedWrite(&value,sizeof(bool),sec,usec);
}
ssize_t filedescriptor::write(char character,
long sec, long usec) const {
DEBUG_WRITE_CHAR("char",character);
return bufferedWrite(&character,sizeof(char),sec,usec);
}
ssize_t filedescriptor::write(const unsigned char *string, size_t size,
long sec, long usec) const {
DEBUG_WRITE_STRING("ustring",string,size);
return bufferedWrite(string,size,sec,usec);
}
ssize_t filedescriptor::write(const char *string, size_t size,
long sec, long usec) const {
DEBUG_WRITE_STRING("string",string,size);
return bufferedWrite(string,size,sec,usec);
}
ssize_t filedescriptor::write(const unsigned char *string,
long sec, long usec) const {
DEBUG_WRITE_STRING("ustring",string,charstring::length(string));
return bufferedWrite(string,charstring::length(string),sec,usec);
}
ssize_t filedescriptor::write(const char *string,
long sec, long usec) const {
DEBUG_WRITE_STRING("string",string,charstring::length(string));
return bufferedWrite(string,charstring::length(string),sec,usec);
}
ssize_t filedescriptor::write(const void *buffer, size_t size,
long sec, long usec) const {
DEBUG_WRITE_VOID("void",buffer,size);
return bufferedWrite(buffer,size,sec,usec);
}
ssize_t filedescriptor::read(uint16_t *buffer) const {
return read(buffer,-1,-1);
}
ssize_t filedescriptor::read(uint32_t *buffer) const {
return read(buffer,-1,-1);
}
ssize_t filedescriptor::read(uint64_t *buffer) const {
return read(buffer,-1,-1);
}
ssize_t filedescriptor::read(int16_t *buffer) const {
return bufferedRead(buffer,sizeof(int16_t),-1,-1);
}
ssize_t filedescriptor::read(int32_t *buffer) const {
return bufferedRead(buffer,sizeof(int32_t),-1,-1);
}
ssize_t filedescriptor::read(int64_t *buffer) const {
return bufferedRead(buffer,sizeof(int64_t),-1,-1);
}
ssize_t filedescriptor::read(float *buffer) const {
return bufferedRead(buffer,sizeof(float),-1,-1);
}
ssize_t filedescriptor::read(double *buffer) const {
return bufferedRead(buffer,sizeof(double),-1,-1);
}
ssize_t filedescriptor::read(unsigned char *buffer) const {
return bufferedRead(buffer,sizeof(unsigned char),-1,-1);
}
ssize_t filedescriptor::read(bool *buffer) const {
return bufferedRead(buffer,sizeof(bool),-1,-1);
}
ssize_t filedescriptor::read(char *buffer) const {
return bufferedRead(buffer,sizeof(char),-1,-1);
}
ssize_t filedescriptor::read(unsigned char *buffer, size_t size) const {
return bufferedRead(buffer,size,-1,-1);
}
ssize_t filedescriptor::read(char *buffer, size_t size) const {
return bufferedRead(buffer,size,-1,-1);
}
ssize_t filedescriptor::read(void *buffer, size_t size) const {
return bufferedRead(buffer,size,-1,-1);
}
ssize_t filedescriptor::read(char **buffer, char *terminator) const {
return read(buffer,terminator,-1,-1);
}
ssize_t filedescriptor::read(uint16_t *buffer,
long sec, long usec) const {
ssize_t retval=bufferedRead(buffer,sizeof(uint16_t),sec,usec);
*buffer=netToHost(*buffer);
return retval;
}
ssize_t filedescriptor::read(uint32_t *buffer,
long sec, long usec) const {
ssize_t retval=bufferedRead(buffer,sizeof(uint32_t),sec,usec);
*buffer=netToHost(*buffer);
return retval;
}
ssize_t filedescriptor::read(uint64_t *buffer,
long sec, long usec) const {
ssize_t retval=bufferedRead(buffer,sizeof(uint64_t),sec,usec);
*buffer=netToHost(*buffer);
return retval;
}
ssize_t filedescriptor::read(int16_t *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(int16_t),sec,usec);
}
ssize_t filedescriptor::read(int32_t *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(int32_t),sec,usec);
}
ssize_t filedescriptor::read(int64_t *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(int64_t),sec,usec);
}
ssize_t filedescriptor::read(float *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(float),sec,usec);
}
ssize_t filedescriptor::read(double *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(double),sec,usec);
}
ssize_t filedescriptor::read(unsigned char *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(unsigned char),sec,usec);
}
ssize_t filedescriptor::read(bool *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(bool),sec,usec);
}
ssize_t filedescriptor::read(char *buffer,
long sec, long usec) const {
return bufferedRead(buffer,sizeof(char),sec,usec);
}
ssize_t filedescriptor::read(unsigned char *buffer, size_t size,
long sec, long usec) const {
return bufferedRead(buffer,size,sec,usec);
}
ssize_t filedescriptor::read(char *buffer, size_t size,
long sec, long usec) const {
return bufferedRead(buffer,size,sec,usec);
}
ssize_t filedescriptor::read(void *buffer, size_t size,
long sec, long usec) const {
return bufferedRead(buffer,size,sec,usec);
}
bool filedescriptor::close() {
#ifdef RUDIMENTS_HAS_SSL
if (pvt->_ssl) {
pvt->_sslresult=SSL_shutdown(pvt->_ssl);
}
#endif
if (pvt->_fd!=-1) {
int result;
do {
result=::close(pvt->_fd);
} while (result==-1 && error::getErrorNumber()==EINTR);
if (result==-1) {
return false;
}
pvt->_fd=-1;
}
return true;
}
void filedescriptor::retryInterruptedReads() {
pvt->_retryinterruptedreads=true;
}
void filedescriptor::dontRetryInterruptedReads() {
pvt->_retryinterruptedreads=false;
}
void filedescriptor::retryInterruptedWrites() {
pvt->_retryinterruptedwrites=true;
}
void filedescriptor::dontRetryInterruptedWrites() {
pvt->_retryinterruptedwrites=false;
}
void filedescriptor::retryInterruptedWaits() {
pvt->_retryinterruptedwaits=true;
}
void filedescriptor::dontRetryInterruptedWaits() {
pvt->_retryinterruptedwaits=false;
}
void filedescriptor::retryInterruptedFcntl() {
#ifdef RUDIMENTS_HAVE_FCNTL
pvt->_retryinterruptedfcntl=true;
#endif
}
void filedescriptor::dontRetryInterruptedFcntl() {
#ifdef RUDIMENTS_HAVE_FCNTL
pvt->_retryinterruptedfcntl=true;
#endif
}
void filedescriptor::retryInterruptedIoctl() {
#ifdef RUDIMENTS_HAVE_IOCTL
pvt->_retryinterruptedioctl=true;
#endif
}
void filedescriptor::dontRetryInterruptedIoctl() {
#ifdef RUDIMENTS_HAVE_IOCTL
pvt->_retryinterruptedioctl=true;
#endif
}
void filedescriptor::allowShortReads() {
pvt->_allowshortreads=true;
}
void filedescriptor::dontAllowShortReads() {
pvt->_allowshortreads=false;
}
void filedescriptor::allowShortWrites() {
pvt->_allowshortwrites=true;
}
void filedescriptor::dontAllowShortWrites() {
pvt->_allowshortwrites=false;
}
void filedescriptor::useListener(listener *lstnr) {
pvt->_lstnr=lstnr;
}
void filedescriptor::useListenerInsideReads() {
pvt->_uselistenerinsidereads=true;
}
void filedescriptor::dontUseListenerInsideReads() {
pvt->_uselistenerinsidereads=false;
}
void filedescriptor::useListenerInsideWrites() {
pvt->_uselistenerinsidewrites=true;
}
void filedescriptor::dontUseListenerInsideWrites() {
pvt->_uselistenerinsidewrites=false;
}
void filedescriptor::dontUseListener() {
pvt->_lstnr=NULL;
}
listener *filedescriptor::getListener() {
return pvt->_lstnr;
}
ssize_t filedescriptor::read(char **buffer, char *terminator,
long sec, long usec) const {
// initialize a buffer
int buffersize=512;
if (buffer) {
*buffer=new char[buffersize];
}
// initialize termination detector
int termlen=charstring::length(terminator);
char *term=new char[termlen];
for (int i=0; i<termlen; i++) {
term[i]='\0';
}
// initialize some variables
char charbuffer;
int sizeread;
int totalread=0;
int escaped=0;
int copytobuffer;
int copytoterm;
// loop, getting 1 character at a time
for (;;) {
// read from the file descriptor
if ((sizeread=bufferedRead(&charbuffer,
sizeof(char),sec,usec))<=RESULT_ERROR) {
totalread=sizeread;
break;
}
totalread=totalread+sizeread;
if (sizeread) {
// handle escaping
if (escaped) {
copytobuffer=1;
copytoterm=0;
escaped=0;
} else {
if (charbuffer=='\\') {
escaped=1;
} else {
escaped=0;
}
if (escaped) {
copytobuffer=0;
copytoterm=0;
} else {
copytobuffer=1;
copytoterm=1;
}
}
// copy to return buffer
if (copytobuffer && buffer) {
// extend buffer if necessary
if (totalread==buffersize) {
char *newbuffer=
new char[buffersize+512];
charstring::copy(newbuffer,
*buffer,buffersize);
delete *buffer;
buffersize=buffersize+512;
*buffer=newbuffer;
}
(*buffer)[totalread-1]=charbuffer;
(*buffer)[totalread]='\0';
}
if (copytoterm) {
// update terminator detector
for (int i=0; i<termlen-1; i++) {
term[i]=term[i+1];
}
term[termlen-1]=charbuffer;
// check for termination
if (!charstring::compare(term,terminator,
termlen)) {
break;
}
} else {
// clear terminator
for (int i=0; i<termlen; i++) {
term[i]='\0';
}
}
} else {
break;
}
}
delete[] term;
return totalread;
}
ssize_t filedescriptor::bufferedRead(void *buf, ssize_t count,
long sec, long usec) const {
#ifdef DEBUG_READ
printf("bufferedRead of %d bytes\n",count);
#endif
if (!count) {
return 0;
}
if (!pvt->_readbuffer) {
#ifdef DEBUG_READ
printf("no read buffer...\n");
#endif
return safeRead(buf,count,sec,usec);
}
unsigned char *data=reinterpret_cast<unsigned char *>(buf);
ssize_t bytesread=0;
ssize_t bytesunread=count;
for (;;) {
// copy what we can from the buffer
ssize_t bytesavailabletocopy=pvt->_readbuffertail-
pvt->_readbufferhead;
if (bytesavailabletocopy) {
#ifdef DEBUG_READ
printf("%d bytes in read buffer\n",
bytesavailabletocopy);
#endif
ssize_t bytestocopy=(bytesavailabletocopy<bytesunread)?
bytesavailabletocopy:
bytesunread;
#ifdef DEBUG_READ
printf("copying %d bytes out of read buffer\n",
bytestocopy);
#endif
rawbuffer::copy(data,pvt->_readbufferhead,bytestocopy);
data=data+bytestocopy;
bytesread=bytesread+bytestocopy;
pvt->_readbufferhead=pvt->_readbufferhead+bytestocopy;
bytesunread=bytesunread-bytestocopy;
// if we've read enough, break out
if (bytesread==count) {
#ifdef DEBUG_READ
printf("yay, we're done reading\n");
#endif
return bytesread;
}
#ifdef DEBUG_READ
printf("need to read %d more bytes\n",bytesunread);
#endif
}
// if we've copied out everything in the buffer, read some more
if (pvt->_readbufferhead==pvt->_readbuffertail) {
#ifdef DEBUG_READ
printf("attempting to fill read buffer, ");
printf("reading %d bytes...\n",
pvt->_readbufferend-pvt->_readbuffer);
#endif
bool saveasr=pvt->_allowshortreads;
pvt->_allowshortreads=true;
ssize_t result=safeRead(pvt->_readbuffer,
pvt->_readbufferend-
pvt->_readbuffer,
sec,usec);
pvt->_allowshortreads=saveasr;
if (!result) {
if (pvt->_allowshortreads) {
#ifdef DEBUG_READ
printf("EOF\n");
#endif
return bytesread;
}
#ifdef DEBUG_READ
printf("still need %d bytes, reading...\n",
bytesunread);
#endif
result=safeRead(pvt->_readbuffer,
bytesunread,
sec,usec);
if (result>-1 && result!=bytesunread) {
#ifdef DEBUG_READ
printf("EOF\n");
#endif
return bytesread;
}
}
if (result<0) {
#ifdef DEBUG_READ
printf("error reading...\n");
#endif
return result;
}
pvt->_readbufferhead=pvt->_readbuffer;
pvt->_readbuffertail=pvt->_readbuffer+result;
#ifdef DEBUG_READ
printf("read %d bytes\n",result);
#endif
}
}
}
ssize_t filedescriptor::safeRead(void *buf, ssize_t count,
long sec, long usec) const {
// FIXME: is this what we want to do?
// maybe we should set some kind of error condition too
if (!buf) {
return 0;
}
#ifdef DEBUG_READ
printf("%d: safeRead(%d,",process::getProcessId(),pvt->_fd);
#endif
// The result of SSL_read may be undefined if count=0
#ifdef RUDIMENTS_HAS_SSL
if (!count) {
return 0;
}
#endif
ssize_t totalread=0;
ssize_t sizetoread;
ssize_t actualread;
while (totalread<count) {
// only read SSIZE_MAX at a time
sizetoread=count-totalread;
if (sizetoread>SSIZE_MAX) {
sizetoread=SSIZE_MAX;
}
// if necessary, select
if (sec>-1 && usec>-1 || pvt->_uselistenerinsidereads) {
int selectresult=(pvt->_uselistenerinsidereads)?
waitForNonBlockingRead(sec,usec):
safeSelect(sec,usec,true,false);
// return error or timeout
if (selectresult<0) {
#ifdef DEBUG_READ
printf(")\n");
#endif
return selectresult;
}
// FIXME: if uselistenerinsidereads is set, and data
// is available on a different file descriptor than
// this one (and none is available on this one),
// return the current size (a short read). Apps should
// respond to this short read condition by checking the
// listener's ready list... maybe?
}
// set a pointer to the position in the buffer that we need
// to read data into
void *ptr=static_cast<void *>(
static_cast<unsigned char *>(buf)+
totalread);
// read...
error::clearError();
#ifdef RUDIMENTS_HAS_SSL
if (pvt->_ssl) {
for (bool done=false; !done ;) {
#ifdef RUDIMENTS_SSL_VOID_PTR
actualread=SSL_read(pvt->_ssl,ptr,sizetoread);
#else
actualread=SSL_read(pvt->_ssl,
static_cast<char *>(ptr),
sizetoread);
#endif
pvt->_sslresult=actualread;
switch (SSL_get_error(pvt->_ssl,actualread)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
continue;
case SSL_ERROR_WANT_X509_LOOKUP:
case SSL_ERROR_SSL:
actualread=-1;
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SYSCALL:
case SSL_ERROR_NONE:
default:
done=true;
}
}
} else {
#endif
actualread=::read(pvt->_fd,ptr,sizetoread);
#ifdef DEBUG_READ
for (int i=0; i<actualread; i++) {
character::safePrint(
(static_cast<unsigned char *>(ptr))[i]);
}
printf("(%ld bytes) ",actualread);
fflush(stdout);
#endif
#ifdef RUDIMENTS_HAS_SSL
}
#endif
// if we didn't read the number of bytes we expected to,
// handle that...
if (actualread!=sizetoread) {
if (error::getErrorNumber()==EINTR) {
#ifdef DEBUG_READ
printf(" EINTR ");
#endif
// if we got an EINTR, then we may need to
// retry the read
if (pvt->_retryinterruptedreads) {
continue;
} else {
totalread=totalread+actualread;
break;
}
} else if (actualread==0 &&
error::getErrorNumber()==0) {
// eof condition
#ifdef DEBUG_READ
printf(" EOF ");
#endif
break;
} else if (actualread==-1) {
// error condition
#ifdef DEBUG_READ
printf(")\n");
#endif
return RESULT_ERROR;
}
}
totalread=totalread+actualread;
// if we want to allow short reads, then break out here
if (pvt->_allowshortreads) {
#ifdef DEBUG_READ
printf(" SHORTREAD ");
#endif
break;
}
}
#ifdef DEBUG_READ
printf(",%d)\n",totalread);
#endif
return totalread;
}
ssize_t filedescriptor::bufferedWrite(const void *buf, ssize_t count,
long sec, long usec) const {
#ifdef DEBUG_WRITE
printf("bufferedWrite of %d bytes\n",count);
#endif
if (!count) {
return 0;
}
if (!pvt->_writebuffer) {
#ifdef DEBUG_WRITE
printf("no write buffer...\n");
#endif
return safeWrite(buf,count,sec,usec);
}
const unsigned char *data=
reinterpret_cast<const unsigned char *>(buf);
ssize_t initialwritebuffersize=pvt->_writebufferptr-pvt->_writebuffer;
bool first=true;
ssize_t byteswritten=0;
ssize_t bytesunwritten=count;
while (byteswritten<count) {
ssize_t writebuffersize=pvt->_writebufferptr-
pvt->_writebuffer;
ssize_t writebufferspace=pvt->_writebufferend-
pvt->_writebufferptr;
#ifdef DEBUG_WRITE
printf(" writebuffersize=%d\n",writebuffersize);
printf(" writebufferspace=%d\n",writebufferspace);
printf(" byteswritten=%d\n",byteswritten);
printf(" bytesunwritten=%d\n",bytesunwritten);
#endif
if (bytesunwritten<=writebufferspace) {
#ifdef DEBUG_WRITE
printf("buffering %d bytes\n",bytesunwritten);
#endif
rawbuffer::copy(pvt->_writebufferptr,
data,bytesunwritten);
pvt->_writebufferptr=pvt->_writebufferptr+
bytesunwritten;
byteswritten=byteswritten+bytesunwritten;
} else {
#ifdef DEBUG_WRITE
printf("just buffering %d bytes\n",writebufferspace);
#endif
rawbuffer::copy(pvt->_writebufferptr,
data,writebufferspace);
bool saveasw=pvt->_allowshortwrites;
pvt->_allowshortwrites=true;
ssize_t result=safeWrite(pvt->_writebuffer,
writebuffersize+writebufferspace,
sec,usec);
pvt->_allowshortwrites=saveasw;
if (result!=writebuffersize+writebufferspace) {
return result;
}
pvt->_writebufferptr=pvt->_writebuffer;
// The first time the buffer is written, the number of
// bytes that were already in the buffer need to be
// taken into account when calculating byteswritten,
// bytesunwritten and data.
ssize_t adjustment=(first)?initialwritebuffersize:0;
if (first) {
first=false;
}
byteswritten=byteswritten+result-adjustment;
bytesunwritten=bytesunwritten-result+adjustment;
data=data+result-adjustment;
}
}
return byteswritten;
}
bool filedescriptor::flushWriteBuffer(long sec, long usec) const {
if (!pvt->_writebuffer) {
return true;
}
ssize_t writebuffersize=pvt->_writebufferptr-pvt->_writebuffer;
bool retval=(safeWrite(pvt->_writebuffer,writebuffersize,
sec,usec)==writebuffersize);
pvt->_writebufferptr=pvt->_writebuffer;
return retval;
}
ssize_t filedescriptor::safeWrite(const void *buf, ssize_t count,
long sec, long usec) const {
// FIXME: is this what we want to do?
// maybe we should set some kind of error condition too
if (!buf) {
return 0;
}
#ifdef DEBUG_WRITE
printf("%d: safeWrite(%d,",process::getProcessId(),pvt->_fd);
#endif
// The result of SSL_write may be undefined if count=0
#ifdef RUDIMENTS_HAS_SSL
if (!count) {
return 0;
}
#endif
ssize_t totalwrite=0;
ssize_t sizetowrite;
ssize_t actualwrite;
while (totalwrite<count) {
// only write SSIZE_MAX at a time
sizetowrite=count-totalwrite;
if (sizetowrite>SSIZE_MAX) {
sizetowrite=SSIZE_MAX;
}
// if necessary, select
if (sec>-1 && usec>-1 || pvt->_uselistenerinsidewrites) {
int selectresult=(pvt->_uselistenerinsidewrites)?
waitForNonBlockingWrite(sec,usec):
safeSelect(sec,usec,false,true);
// return error or timeout
if (selectresult<0) {
#ifdef DEBUG_WRITE
printf(")\n");
#endif
return selectresult;
}
}
// set a pointer to the position in the buffer that we need
// to write data from
const void *ptr=static_cast<const void *>(
static_cast<const unsigned char *>(buf)+
totalwrite);
error::clearError();
#ifdef RUDIMENTS_HAS_SSL
if (pvt->_ssl) {
for (bool done=false; !done ;) {
#ifdef RUDIMENTS_SSL_VOID_PTR
actualwrite=::SSL_write(pvt->_ssl,ptr,count);
#else
actualwrite=::SSL_write(pvt->_ssl,
static_cast<const char *>(ptr),
sizetowrite);
#endif
pvt->_sslresult=actualwrite;
switch (SSL_get_error(pvt->_ssl,actualwrite)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
continue;
case SSL_ERROR_WANT_X509_LOOKUP:
case SSL_ERROR_SSL:
actualwrite=-1;
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SYSCALL:
case SSL_ERROR_NONE:
default:
done=true;
}
}
} else {
#endif
actualwrite=::write(pvt->_fd,ptr,sizetowrite);
#ifdef DEBUG_WRITE
for (int i=0; i<actualwrite; i++) {
character::safePrint(
(static_cast<const unsigned char *>(ptr))[i]);
}
printf("(%ld bytes) ",actualwrite);
fflush(stdout);
#endif
#ifdef RUDIMENTS_HAS_SSL
}
#endif
// if we didn't read the number of bytes we expected to,
// handle that...
if (actualwrite!=sizetowrite) {
if (error::getErrorNumber()==EINTR) {
#ifdef DEBUG_WRITE
printf(" EINTR ");
#endif
// if we got an EINTR, then we may need to
// retry the write
if (pvt->_retryinterruptedwrites) {
continue;
} else {
totalwrite=totalwrite+actualwrite;
break;
}
} else if (actualwrite==0 &&
error::getErrorNumber()==0) {
// eof condition
#ifdef DEBUG_WRITE
printf(" EOF ");
#endif
break;
} else if (actualwrite==-1) {
// error condition
#ifdef DEBUG_WRITE
printf(")\n");
#endif
return RESULT_ERROR;
}
}
totalwrite=totalwrite+actualwrite;
// if we want to allow short writes, then break out here
if (pvt->_allowshortwrites) {
#ifdef DEBUG_WRITE
printf(" SHORTWRITE ");
#endif
break;
}
}
#ifdef DEBUG_WRITE
printf(",%d)\n",totalwrite);
#endif
return totalwrite;
}
int filedescriptor::waitForNonBlockingRead(long sec, long usec) const {
return (pvt->_lstnr)?pvt->_lstnr->waitForNonBlockingRead(sec,usec):
safeSelect(sec,usec,true,false);
}
int filedescriptor::waitForNonBlockingWrite(long sec, long usec) const {
return (pvt->_lstnr)?pvt->_lstnr->waitForNonBlockingWrite(sec,usec):
safeSelect(sec,usec,false,true);
}
int filedescriptor::safeSelect(long sec, long usec,
bool read, bool write) const {
// FD_SET will crash if you pass it -1 on some systems, just bail here
if (pvt->_fd==-1) {
return RESULT_ERROR;
}
#ifdef RUDIMENTS_HAS_SSL
if (read && pvt->_ssl && SSL_pending(pvt->_ssl)) {
return 1;
}
#endif
// set up the timeout
timeval tv;
timeval *tvptr=(sec>-1 && usec>-1)?&tv:NULL;
for (;;) {
// some versions of select modify the timeout, so reset it
// every time
tv.tv_sec=sec;
tv.tv_usec=usec;
// select() will modify the list every time it's called
// so the list has to be rebuilt every time...
fd_set fdlist;
FD_ZERO(&fdlist);
FD_SET(pvt->_fd,&fdlist);
// wait for data to be available on the file descriptor
int selectresult=select(pvt->_fd+1,(read)?&fdlist:NULL,
(write)?&fdlist:NULL,
NULL,tvptr);
if (selectresult==-1) {
// if a signal caused the select to fall through, retry
if (pvt->_retryinterruptedwaits &&
error::getErrorNumber()==EINTR) {
continue;
}
return RESULT_ERROR;
} else if (!selectresult) {
// timeout
return RESULT_TIMEOUT;
}
return selectresult;
}
}
void filedescriptor::translateByteOrder() {
pvt->_translatebyteorder=true;
}
void filedescriptor::dontTranslateByteOrder() {
pvt->_translatebyteorder=false;
}
uint16_t filedescriptor::hostToNet(uint16_t value) const {
return (pvt->_translatebyteorder)?htons(value):value;
}
uint32_t filedescriptor::hostToNet(uint32_t value) const {
return (pvt->_translatebyteorder)?htonl(value):value;
}
uint64_t filedescriptor::hostToNet(uint64_t value) const {
#if __BYTE_ORDER == __BIG_ENDIAN
return value;
#else
#if defined(RUDIMENTS_HAVE_BSWAP_64)
return bswap_64(value);
#elif defined(RUDIMENTS_HAVE___BSWAP64)
return __bswap64(value);
#elif defined(RUDIMENTS_HAVE_BSWAP64)
return bswap64(value);
#elif defined(RUDIMENTS_HAVE_SWAP64)
return swap64(value);
#elif defined(RUDIMENTS_HAVE_OSSWAPHOSTTOLITTLEINT64)
return OSSwapHostToLittleInt64(value);
#else
#error no bswap64() or anything like it
#endif
#endif
}
uint16_t filedescriptor::netToHost(uint16_t value) const {
return (pvt->_translatebyteorder)?ntohs(value):value;
}
uint32_t filedescriptor::netToHost(uint32_t value) const {
return (pvt->_translatebyteorder)?ntohl(value):value;
}
uint64_t filedescriptor::netToHost(uint64_t value) const {
#if __BYTE_ORDER == __BIG_ENDIAN
return value;
#else
#if defined(RUDIMENTS_HAVE_BSWAP_64)
return bswap_64(value);
#elif defined(RUDIMENTS_HAVE___BSWAP64)
return __bswap64(value);
#elif defined(RUDIMENTS_HAVE_BSWAP64)
return bswap64(value);
#elif defined(RUDIMENTS_HAVE_SWAP64)
return swap64(value);
#elif defined(RUDIMENTS_HAVE_OSSWAPLITTLETOHOSTINT64)
return OSSwapLittleToHostInt64(value);
#else
#error no bswap64() or anything like it
#endif
#endif
}
#ifdef RUDIMENTS_HAS_SSL
int filedescriptor::getSSLResult() const {
return pvt->_sslresult;
}
#endif
int filedescriptor::fcntl(int cmd, long arg) const {
#ifdef RUDIMENTS_HAVE_FCNTL
int result;
do {
result=::fcntl(pvt->_fd,cmd,arg);
} while (pvt->_retryinterruptedfcntl && result==-1 &&
error::getErrorNumber()==EINTR);
return result;
#else
return -1;
#endif
}
int filedescriptor::ioctl(int cmd, void *arg) const {
#ifdef RUDIMENTS_HAVE_IOCTL
int result;
do {
result=::ioctl(pvt->_fd,cmd,arg);
} while (pvt->_retryinterruptedioctl && result==-1 &&
error::getErrorNumber()==EINTR);
return result;
#else
return -1;
#endif
}
#ifdef __MINGW32__
bool filedescriptor::passFileDescriptor(int filedesc) const {
return false;
}
bool filedescriptor::receiveFileDescriptor(int *filedesc) const {
return false;
}
#else
bool filedescriptor::passFileDescriptor(int filedesc) const {
// have to use sendmsg to pass a file descriptor.
// sendmsg can only send a msghdr
struct msghdr messageheader;
// these must be null for stream sockets
messageheader.msg_name=NULL;
messageheader.msg_namelen=0;
#ifdef RUDIMENTS_HAVE_MSGHDR_MSG_FLAGS
// initialize flags to 0
messageheader.msg_flags=0;
#endif
// must send at least 1 iovector with 1 byte of real data
struct iovec iovector[1];
iovector[0].iov_base=(RUDIMENTS_IOV_BASE_TYPE)" ";
iovector[0].iov_len=sizeof(char);
messageheader.msg_iov=iovector;
messageheader.msg_iovlen=1;
// use other parts of the msghdr structure to send the descriptor
#ifdef RUDIMENTS_HAVE_MSGHDR_MSG_CONTROLLEN
// new-style:
// The descriptor is passed in the msg_control
//#ifdef RUDIMENTS_HAVE_CMSG_SPACE
//union {
//struct cmsghdr cm;
//char control[CMSG_SPACE(sizeof(int))];
//} control;
//messageheader.msg_control=control.control;
//#else
unsigned char control[sizeof(struct cmsghdr)+sizeof(int)];
messageheader.msg_control=(caddr_t)control;
//#endif
messageheader.msg_controllen=sizeof(control);
struct cmsghdr *cmptr;
cmptr=CMSG_FIRSTHDR(&messageheader);
cmptr->cmsg_level=SOL_SOCKET;
cmptr->cmsg_type=SCM_RIGHTS;
//#ifdef RUDIMENTS_HAVE_CMSG_LEN
//cmptr->cmsg_len=CMSG_LEN(sizeof(int));
//#else
cmptr->cmsg_len=sizeof(control);
//#endif
*(reinterpret_cast<int *>(CMSG_DATA(cmptr)))=filedesc;
// FIXME: is this necessary???
messageheader.msg_controllen=cmptr->cmsg_len;
#else
// old-style:
// The descriptor is passed in the accrights
messageheader.msg_accrights=(caddr_t)&filedesc;
messageheader.msg_accrightslen=sizeof(int);
#endif
// finally, send the msghdr
int result;
do {
result=sendmsg(pvt->_fd,&messageheader,0);
} while (result==-1 && error::getErrorNumber()==EINTR);
return (result!=-1);
}
bool filedescriptor::receiveFileDescriptor(int *filedesc) const {
// have to use recvmsg to receive a file descriptor.
// recvmsg can only send a msghdr
struct msghdr messageheader;
// these must be null for stream sockets
messageheader.msg_name=NULL;
messageheader.msg_namelen=0;
#ifdef RUDIMENTS_HAVE_MSGHDR_MSG_FLAGS
// initialize flags to 0
messageheader.msg_flags=0;
#endif
// the process that's going to handoff it's socket will also
// send a single iovector with a single byte of data in it,
// so we'll receive that too
struct iovec iovector[1];
char ptr;
iovector[0].iov_base=(RUDIMENTS_IOV_BASE_TYPE)&ptr;
iovector[0].iov_len=sizeof(char);
messageheader.msg_iov=iovector;
messageheader.msg_iovlen=1;
#ifdef RUDIMENTS_HAVE_MSGHDR_MSG_CONTROLLEN
// new-style:
// The descriptor is received in the msg_control
//#ifdef RUDIMENTS_HAVE_CMSG_SPACE
//union {
//struct cmsghdr cm;
//char control[CMSG_SPACE(sizeof(int))];
//} control;
//messageheader.msg_control=control.control;
//#else
unsigned char control[sizeof(struct cmsghdr)+sizeof(int)];
messageheader.msg_control=(caddr_t)control;
//#endif
messageheader.msg_controllen=sizeof(control);
#else
// old-style
// The descriptor is received in the accrights
int newfiledesc;
messageheader.msg_accrights=(caddr_t)&newfiledesc;
messageheader.msg_accrightslen=sizeof(int);
#endif
// receive the msghdr
int result;
do {
result=recvmsg(pvt->_fd,&messageheader,0);
} while (result==-1 && error::getErrorNumber()==EINTR);
if (result==-1) {
return false;
}
// if we got valid data, set the passed-in descriptor to the
// descriptor we received and return success
#ifdef RUDIMENTS_HAVE_MSGHDR_MSG_CONTROLLEN
struct cmsghdr *cmptr=CMSG_FIRSTHDR(&messageheader);
if (cmptr && cmptr->cmsg_len==
//#ifdef RUDIMENTS_HAVE_CMSG_LEN
//CMSG_LEN(sizeof(int)) &&
//#else
//(sizeof(struct cmsghdr)+sizeof(int)) &&
sizeof(control) &&
//#endif
cmptr->cmsg_level==SOL_SOCKET &&
cmptr->cmsg_type==SCM_RIGHTS) {
// if we got good data, set the descriptor and return
*filedesc=*(reinterpret_cast<int *>(CMSG_DATA(cmptr)));
return true;
}
#ifdef DEBUG_PASSFD
else {
// if we got bad data, be specific about what was
// wrong, this will help debug problems with different
// platforms
if (!cmptr) {
printf("%d: ",process::getProcessId());
printf("null cmptr\n");
} else {
//#ifdef RUDIMENTS_HAVE_CMSG_LEN
/*if (cmptr->cmsg_len!=CMSG_LEN(sizeof(int))) {
printf("%d: ",process::getProcessId());
printf("got cmsg_len=");
printf("%d",static_cast<long>(
cmptr->cmsg_len));
printf(" instead of ");
printf("%d",static_cast<long>(
CMSG_LEN(sizeof(int))));
printf("\n");
}*/
//#endif
if (cmptr->cmsg_level!=SOL_SOCKET) {
printf("%d: ",process::getProcessId());
printf("got cmsg_level=");
printf("%d",static_cast<long>(
cmptr->cmsg_level));
printf(" instead of ");
printf("%d",static_cast<long>(
SOL_SOCKET));
printf("\n");
}
if (cmptr->cmsg_type!=SCM_RIGHTS) {
printf("%d: ",process::getProcessId());
printf("got cmsg_type=");
printf("%d",static_cast<long>(
cmptr->cmsg_type));
printf(" instead of ");
printf("%d",static_cast<long>(
SCM_RIGHTS));
printf("\n");
}
}
}
#endif
#else
if (messageheader.msg_accrightslen==sizeof(int)) {
*filedesc=newfiledesc;
return true;
}
#endif
// if we're here then we must have received some bad data
return false;
}
#endif
bool filedescriptor::useNaglesAlgorithm() {
return setNoDelay(0);
}
bool filedescriptor::dontUseNaglesAlgorithm() {
return setNoDelay(1);
}
bool filedescriptor::setNoDelay(int onoff) {
int value=onoff;
return !setSockOpt(IPPROTO_TCP,TCP_NODELAY,
(RUDIMENTS_SETSOCKOPT_OPTVAL_TYPE)&value,
(socklen_t)sizeof(int));
}
bool filedescriptor::getTcpWriteBufferSize(int *size) {
socklen_t intsize=sizeof(int);
return getSockOpt(SOL_SOCKET,SO_SNDBUF,
(RUDIMENTS_GETSOCKOPT_OPTVAL_TYPE)size,
&intsize)!=-1;
}
bool filedescriptor::setTcpWriteBufferSize(int size) {
return !setSockOpt(SOL_SOCKET,SO_SNDBUF,
(RUDIMENTS_SETSOCKOPT_OPTVAL_TYPE)&size,
static_cast<socklen_t>(sizeof(int)));
}
bool filedescriptor::getTcpReadBufferSize(int *size) {
socklen_t intsize=sizeof(int);
return getSockOpt(SOL_SOCKET,SO_RCVBUF,
(RUDIMENTS_GETSOCKOPT_OPTVAL_TYPE)size,
&intsize)!=-1;
}
bool filedescriptor::setTcpReadBufferSize(int size) {
return setSockOpt(SOL_SOCKET,SO_RCVBUF,
(RUDIMENTS_SETSOCKOPT_OPTVAL_TYPE)&size,
static_cast<socklen_t>(sizeof(int)))!=-1;
}
const char *filedescriptor::getType() const {
return pvt->_type;
}
char *filedescriptor::getPeerAddress() const {
// initialize a socket address structure
struct sockaddr_in clientsin;
socklen_t size=sizeof(clientsin);
rawbuffer::zero(&clientsin,sizeof(clientsin));
// get the peer address
int result;
do {
result=getpeername(pvt->_fd,
reinterpret_cast<struct sockaddr *>(&clientsin),
&size);
} while (result==-1 && error::getErrorNumber()==EINTR);
if (result==-1) {
return NULL;
}
// convert the address to a string and return a copy of it
return charstring::duplicate(inet_ntoa(clientsin.sin_addr));
}
int filedescriptor::getSockOpt(int level, int optname,
void *optval, socklen_t *optlen) {
int result;
do {
result=getsockopt(pvt->_fd,level,optname,optval,optlen);
} while (result==-1 && error::getErrorNumber()==EINTR);
return result;
}
int filedescriptor::setSockOpt(int level, int optname,
const void *optval, socklen_t optlen) {
int result;
do {
result=setsockopt(pvt->_fd,level,optname,optval,optlen);
} while (result==-1 && error::getErrorNumber()==EINTR);
return result;
}
const char *filedescriptor::type() const {
return pvt->_type;
}
void filedescriptor::type(const char *tp) {
pvt->_type=tp;
}
int filedescriptor::fd() const {
return pvt->_fd;
}
void filedescriptor::fd(int filedes) {
pvt->_fd=filedes;
}
#ifdef RUDIMENTS_HAS_SSL
SSL_CTX *filedescriptor::ctx() {
return pvt->_ctx;
}
SSL *filedescriptor::ssl() {
return pvt->_ssl;
}
int filedescriptor::sslresult() {
return pvt->_sslresult;
}
void filedescriptor::sslresult(int sslrslt) {
pvt->_sslresult=sslrslt;
}
#endif
bool filedescriptor::closeOnExec() {
return !fcntl(F_SETFD,fcntl(F_GETFD,FD_CLOEXEC)|FD_CLOEXEC);
}
bool filedescriptor::dontCloseOnExec() {
return !fcntl(F_SETFD,fcntl(F_GETFD,FD_CLOEXEC)&(~FD_CLOEXEC));
}
bool filedescriptor::getCloseOnExec() {
return fcntl(F_GETFD,FD_CLOEXEC);
}
#ifdef RUDIMENTS_NAMESPACE
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1