/*******************************************************************************
* BSocket.cc BSocket Classes
* T.Barnaby, BEAM Ltd, 1/4/05
*******************************************************************************
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include "BSocket.h"
#if HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif
#ifndef IP_MTU
#define IP_MTU 14
#endif
BSocketAddress::BSocketAddress(){
olen = 0;
oaddress = 0;
}
BSocketAddress::BSocketAddress(const BSocketAddress& add){
olen = 0;
oaddress = 0;
set(add.oaddress, add.olen);
}
BSocketAddress::BSocketAddress(SockAddr* address, int len){
olen = 0;
oaddress = 0;
set(address, len);
}
BSocketAddress::~BSocketAddress(){
free(oaddress);
oaddress = 0;
olen = 0;;
}
BError BSocketAddress::set(SockAddr* address, int len){
BError err;
if(olen != len){
olen = len;
if(oaddress)
free(oaddress);
oaddress = (SockAddr*)malloc(olen);
}
memcpy(oaddress, address, olen);
return err;
}
const BSocketAddress::SockAddr* BSocketAddress::raw() const {
return oaddress;
}
int BSocketAddress::len() const{
return olen;
}
BSocketAddress& BSocketAddress::operator=(const BSocketAddress& add){
if(this != &add){
free(oaddress);
olen = add.olen;
oaddress = (SockAddr*)malloc(olen);
memcpy(oaddress, add.oaddress, olen);
}
return *this;
}
int BSocketAddress::operator==(const BSocketAddress& add) const {
return !memcmp(oaddress, add.oaddress, olen);
}
int BSocketAddress::operator!=(const BSocketAddress& add) const {
return ! (*this == add);
}
BError BSocketAddressINET::set(BString hostName, uint32_t port){
BError err;
struct hostent* host;
SockAddrIP add;
/* Clear out address structure */
memset(&add, 0, sizeof(add));
add.sin_family = AF_INET;
add.sin_port = htons(port);
if(hostName.len()){
if((host = gethostbyname(hostName)) && host->h_addr_list[0]){
memcpy(&(add.sin_addr.s_addr), host->h_addr_list[0], sizeof(add.sin_addr.s_addr));
}
else {
err.set(-h_errno, hstrerror(h_errno));
}
}
if(!err)
err = BSocketAddress::set((SockAddr*)&add, sizeof(add));
return err;
}
BError BSocketAddressINET::set(uint32_t address, uint32_t port){
BError err;
SockAddrIP add;
/* Clear out address structure */
memset(&add, 0, sizeof(add));
add.sin_family = AF_INET;
add.sin_port = htons(port);
add.sin_addr.s_addr = htonl(address);
err = BSocketAddress::set((SockAddr*)&add, sizeof(add));
return err;
}
BError BSocketAddressINET::set(BString hostName, BString service, BString type){
BError err;
struct servent* ser;
if(ser = getservbyname(service.retStr(), type.retStr())){
err = set(hostName, htons(ser->s_port));
}
else {
err.set(-1, "Getservbyname error");
}
return err;
}
void BSocketAddressINET::setPort(uint32_t port){
SockAddrIP add;
/* Clear out address structure */
memset(&add, 0, sizeof(add));
if(len())
memcpy(&add, raw(), sizeof(add));
add.sin_port = htons(port);
BSocketAddress::set((SockAddr*)&add, sizeof(add));
}
uint32_t BSocketAddressINET::address(){
uint32_t ret = 0;
if(len()){
ret = ntohl(((SockAddrIP*)raw())->sin_addr.s_addr);
}
return ret;
}
uint32_t BSocketAddressINET::port(){
uint32_t ret = 0;
if(len()){
ret = ntohs(((SockAddrIP*)raw())->sin_port);
}
return ret;
}
BString BSocketAddressINET::getString(){
BString ret;
char buf[32];
SockAddrIP* add = (SockAddrIP*)raw();
buf[0] = '\0';
if(len()){
inet_ntop(add->sin_family, &add->sin_addr, buf, sizeof(buf));
ret = BString(buf) + ":" + ntohs(add->sin_port);
}
return ret;
}
// Some usefull functions
BString BSocketAddressINET::getHostName(){
char s[256];
gethostname(s, sizeof(s));
s[sizeof(s) - 1] = '\0';
return s;
}
BList<uint32_t> BSocketAddressINET::getIpAddresses(){
struct hostent* host;
int n;
BList<uint32_t> l;
if(host = gethostbyname(getHostName())){
for(n = 0; host->h_addr_list[n]; n++){
l.append(ntohl(*((int*)host->h_addr_list[n])));
}
}
return l;
}
BList<BString> BSocketAddressINET::getIpAddressList(){
BList<BString> l;
char buf[256] = "";
struct hostent* host;
int n;
if(host = gethostbyname(getHostName())){
for(n = 0; host->h_addr_list[n]; n++){
if(inet_ntop(AF_INET, host->h_addr_list[n], buf, sizeof(buf))){
l.append(buf);
}
}
}
return l;
}
BList<BString> BSocketAddressINET::getIpAddressListAll(){
BList<BString> l;
char buf[256] = "";
#if HAVE_GETIFADDRS
struct ifaddrs* addrs = NULL;
struct ifaddrs* addr = NULL;
struct sockaddr_in* ia;
if(getifaddrs(&addrs) == 0){
for(addr = addrs; addr; addr = addr->ifa_next){
if(addr->ifa_addr && (addr->ifa_addr->sa_family == PF_INET)){
ia = (struct sockaddr_in*)addr->ifa_addr;
if(inet_ntop(AF_INET, &ia->sin_addr, buf, sizeof(buf))){
l.append(buf);
}
}
}
}
#else
struct hostent* host;
int n;
if(host = gethostbyname(getHostName())){
for(n = 0; host->h_addr_list[n]; n++){
if(inet_ntop(AF_INET, host->h_addr_list[n], buf, sizeof(buf))){
l.append(buf);
}
}
}
#endif
return l;
}
/*******************************************************************************
* BSocket class
*******************************************************************************
*/
BSocket::BSocket(){
osocket = -1;
}
BSocket::BSocket(int fd){
osocket = fd;
}
BSocket::BSocket(NType type){
init(type);
if(osocket < 0)
fprintf(stderr, "BSocket::BSocket: Socket creation error: %s\n", strerror(errno));
}
BSocket::~BSocket(){
if(osocket >= 0)
::close(osocket);
osocket = -1;
}
BError BSocket::init(NType type){
BError err;
switch(type){
case STREAM: osocket = socket(AF_INET, SOCK_STREAM, 0); break;
case DGRAM: osocket = socket(AF_INET, SOCK_DGRAM, 0); break;
}
if(osocket < 0)
err.set(-errno, strerror(errno));
return err;
}
int BSocket::getFd(){
return osocket;
}
BError BSocket::bind(const BSocketAddress& address){
BError err;
if(::bind(osocket, address, address.len()) < 0)
err.set(-errno, strerror(errno));
return err;
}
BError BSocket::connect(const BSocketAddress& address){
BError err;
if(::connect(osocket, address, address.len()) < 0)
err.set(-errno, strerror(errno));
return err;
}
BError BSocket::shutdown(int how){
BError err;
if(::shutdown(osocket, how) < 0)
err.set(-errno, strerror(errno));
return err;
}
BError BSocket::close(){
BError err;
if(osocket >= 0)
::close(osocket);
osocket = -1;
return err;
}
BError BSocket::listen(int backlog){
BError err;
if(::listen(osocket, 5) < 0)
err.set(-errno, strerror(errno));
return err;
}
BError BSocket::accept(int& fd){
BError err;
int f;
if((f = ::accept(osocket, 0, 0)) < 0){
err.set(-errno, strerror(errno));
}
else {
fd = f;
}
return err;
}
BError BSocket::accept(int& fd, BSocketAddress& address){
BError err;
BSocketAddress::SockAddr add;
socklen_t len = sizeof(add);
int f;
if((f = ::accept(osocket, &add, &len)) < 0){
err.set(-errno, strerror(errno));
}
else {
fd = f;
address.set(&add, len);
}
return err;
}
BError BSocket::send(const void* buf, BSize nbytes, BSize& nbytesSent, int flags){
BError err;
int r;
if((r = ::send(osocket, buf, nbytes, flags)) < 0){
err.set(-errno, strerror(errno));
}
else {
nbytesSent = r;
}
return err;
}
BError BSocket::sendTo(const BSocketAddress& address, const void* buf, BSize nbytes, BSize& nbytesSent, int flags){
BError err;
int r;
if((r = ::sendto(osocket, buf, nbytes, flags, address, address.len())) < 0){
err.set(-errno, strerror(errno));
}
else {
nbytesSent = r;
}
return err;
}
BError BSocket::recv(void* buf, BSize maxbytes, BSize& nbytesRecv, int flags){
BError err;
int r;
if((r = ::recv(osocket, buf, maxbytes, flags)) < 0){
err.set(-errno, strerror(errno));
}
else {
if(r == 0)
err.set(-EPIPE, "Connection Closed by Peer");
nbytesRecv = r;
}
return err;
}
BError BSocket::recvWithTimeout(void* buf, BSize maxbytes, BSize& nbytesRecv, int timeout, int flags){
BError err;
int r;
fd_set set;
struct timeval t;
if(timeout >= 0){
t.tv_sec = 0;
t.tv_usec = timeout;
FD_ZERO(&set);
FD_SET(osocket, &set);
r = select(osocket + 1, &set, 0, 0, &t);
if(r == 0)
return err.set(-ETIMEDOUT, "Connection timed out");
}
return recv(buf, maxbytes, nbytesRecv, flags);
}
BError BSocket::recvFrom(BSocketAddress& address, void* buf, BSize maxbytes, BSize& nbytesRecv, int flags){
BError err;
int r;
BSocketAddress::SockAddr add;
socklen_t len = sizeof(add);
if((r = ::recvfrom(osocket, buf, maxbytes, flags, &add, &len)) < 0){
err.set(-errno, strerror(errno));
}
else {
address.set(&add, len);
}
return err;
}
BError BSocket::recvFromWithTimeout(BSocketAddress& address, void* buf, BSize maxbytes, BSize& nbytesRecv, int timeout, int flags){
BError err;
int r;
fd_set set;
struct timeval t;
if(timeout >= 0){
t.tv_sec = 0;
t.tv_usec = timeout;
FD_ZERO(&set);
FD_SET(osocket, &set);
r = select(osocket + 1, &set, 0, 0, &t);
if(r == 0)
return err.set(-ETIMEDOUT, "Connection timed out");
}
return recvFrom(address, buf, maxbytes, nbytesRecv, flags);
}
BError BSocket::setSockOpt(int level, int optname, void* optval, unsigned int optlen){
BError err;
if(setsockopt(osocket, level, optname, optval, optlen) < 0)
err.set(-errno, strerror(errno));
return err;
}
BError BSocket::getSockOpt(int level, int optname, void* optval, unsigned int* optlen){
BError err;
if(getsockopt(osocket, level, optname, optval, optlen) < 0)
err.set(-errno, strerror(errno));
return err;
}
BError BSocket::setReuseAddress(int on){
return setSockOpt(SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
}
BError BSocket::setBroadCast(int on){
return setSockOpt(SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
}
BError BSocket::setPriority(Priority priority){
unsigned int level;
switch(priority){
case PriorityLow: level = 2; break;
case PriorityNormal: level = 0; break;
case PriorityHigh: level = 6; break;
default: level = 0; break;
}
return setSockOpt(SOL_SOCKET, SO_PRIORITY, &level, sizeof(level));
}
BError BSocket::getMTU(uint32_t& mtu){
socklen_t mtulen = sizeof(mtu);
return getSockOpt(SOL_IP, IP_MTU, &mtu, &mtulen);
}
BError BSocket::getAddress(BSocketAddress& address){
BError err;
BSocketAddress::SockAddr add;
socklen_t len = sizeof(add);
if(getsockname(osocket, &add, &len) < 0){
err.set(-errno, strerror(errno));
}
else {
address.set(&add, len);
}
return err;
}