/* * Copyright (c) 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #pragma implementation #include "Socket.h" #include #include #include #include #include #ifdef PROTOGEN # include "exception.h" #else # include # define ERROR(fmt, args...) ({fprintf(stderr, fmt "\n" , ## args); exit(1);}) # define ERROS(fmt, args...) ({fprintf(stderr, fmt ": " , ## args); \ perror(""); exit(1);}) # define FATAL(fmt, args...) ({fprintf(stderr, fmt "\n" , ## args); abort();}) # define WARNING(fmt, args...) ({ fprintf(stderr, fmt "\n" , ## args); }) #endif /********************************************************************** * address resolving **********************************************************************/ const char *addrtok(const char *address, const char **ptype, size_t *ptype_len, const char **pparam, size_t *pparam_len) { const char *type; size_t type_len; const char *param; size_t param_len; if(address == NULL) return NULL; const char *p = address; // remove leading blanks while(*p!=0 && isspace(*p)) p++; type = p; // look for end of type while(*p!=0 && *p!='(' && *p!=':') p++; if(p == type) type_len = 0; else // trunc trailing blanks { const char *q = p - 1; while(isspace(*q) && q > type) q--; type_len = q - type + 1; } if(*p == '(') // parameters follow { p++; // remove leading blanks while(*p!=0 && *p!=')' && isspace(*p)) p++; param = p; int level = 0; // allow for nested parentheses while(*p!=0 && (*p!=')' || level>0)) { if(*p=='(') level++; if(*p==')') level--; p++; } if(p == param) param_len = 0; else // trunc trailing blanks { const char *q = p - 1; while(isspace(*q) && q > param) q--; param_len = q - param + 1; } if(param_len == 0) param = NULL; } else { param = NULL; param_len = 0; } // seek for end of step while(*p!=':' && *p!=0) p++; if(*p == ':') p++; // set variable arguments if(ptype != NULL) *ptype = type; if(ptype_len != NULL) *ptype_len = type_len; if(pparam != NULL) *pparam = param; if(pparam_len != NULL) *pparam_len = param_len; // return next step if(*p == 0) return NULL; else return p; } Socket *Socket::ctor(const char *address) // the top level constructor builds a new chain of Socket like objects { if(address == NULL) ERROR("no address"); const char *type; const char *param; size_t type_len; size_t param_len; address = addrtok(address, &type, &type_len, ¶m, ¶m_len); const type_entry *te = lookup_type(type, type_len); if(te == NULL) ERROR("unknown type `%s'", type); return te->ctor(address, param, param_len); } const Socket::type_entry* Socket::lookup_type(const char *name, size_t len) // lookup a type in the table of specializations of class Socket { for(int i = 0; i < n_types; i++) { const type_entry *te = &types[i]; if(tolower(te->name[0]) == tolower(name[0])) if(strncasecmp(&te->name[1], &name[1], len - 1) == 0) if(te->name[len] == 0) return te; } return NULL; } /********************************************************************** * ctor, dtor, and copy methods **********************************************************************/ Socket::Socket() { _lower_level = NULL; _state_plus = 0; _state_minus = 0; } // Used with accept(): Socket ctrl(address); Socket s(ctrl.accept()); Socket::Socket(Socket *s) { _lower_level = s; _state_plus = 0; _state_minus = 0; } Socket::Socket(const char *address) { _lower_level = ctor(address); _state_plus = 0; _state_minus = 0; } Socket::~Socket() { if(_lower_level != NULL) delete _lower_level; } Socket::flags_t Socket::capabilities() const { if(_lower_level == NULL) return cap_plus(); else return _lower_level->capabilities() & ~cap_minus() | cap_plus(); } Socket::flags_t Socket::state() const { if(_lower_level == NULL) return _state_plus; else return _lower_level->state() & ~_state_minus | _state_plus; } void Socket::setstate(flags_t plus, flags_t minus) { _state_plus = _state_plus & ~minus | plus; _state_minus = _state_minus & ~(plus | minus); if(_lower_level != NULL) _lower_level->setstate(plus, minus); } /********************************************************************** * socket methods **********************************************************************/ Socket *Socket::accept(flags_t mode) { Socket *_sock = ctor(); // top level `class Socket' object returns NULL if(capabilities() & accept_able) { if(_lower_level == NULL) { FATAL("accept was not implemented in last socket " "on an accept_able chain"); } else { if(_sock != NULL) { _sock->_lower_level = _lower_level->accept(mode); return _sock; } else return _lower_level->accept(mode); } } else { // ERROR("illegal operation"); // let's try to connect then! if(_lower_level != NULL) { if(_sock != NULL) { // just build the new chain _sock->_lower_level = _lower_level->accept(mode); return _sock; } else return _lower_level->accept(mode); } else { if(_sock != NULL) { _sock->connect(mode); // last socket connects return _sock; } else { FATAL("last socket on chain is abstract"); } } } } bool Socket::connect(flags_t mode) { if(_lower_level != NULL) return _lower_level->connect(mode); else FATAL("not implementeted"); } size_t Socket::send(const char *buf, size_t len, int msgf) { if(_lower_level != NULL) return _lower_level->send(buf, len, msgf); else FATAL("not implementeted"); } size_t Socket::recv(char *buf, size_t max, int msgf) { if(_lower_level != NULL) return _lower_level->recv(buf, max, msgf); else FATAL("not implementeted"); } void Socket::shutdown(flags_t mode) { if(_lower_level != NULL) _lower_level->shutdown(mode); else FATAL("not implementeted"); } void Socket::close(flags_t mode) { if(_lower_level != NULL) _lower_level->close(mode); else FATAL("not implementeted"); } bool Socket::is_sendready(int sec, int usec) const { if(_lower_level != NULL) return _lower_level->is_sendready(sec, usec); else FATAL("not implementeted"); } bool Socket::is_recvready(int sec, int usec) const { if(_lower_level != NULL) return _lower_level->is_recvready(sec, usec); else FATAL("not implementeted"); } bool Socket::is_exception(int sec, int usec) const { if(_lower_level != NULL) return _lower_level->is_exception(sec, usec); else FATAL("not implementeted"); } bool Socket::async(bool, pid_t) { if(! (capabilities() & async_mode)) ERROR("illegal operation"); return state() & async_mode; } bool Socket::noblock(bool) { if(! (capabilities() & async_mode)) ERROR("illegal operation"); return state() & noblock_mode; } #ifdef SELECT_HACK bool Socket::is_sendready(int sec, int usec) const { FdSet rfds; FdSet tfds = fds(); select_again: int n = tfds.select(NULL, &rfds, NULL, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; } bool Socket::is_recvready(int sec, int usec) const { FdSet rfds; FdSet tfds = fds(); select_again: int n = tfds.select(&rfds, NULL, NULL, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; } bool Socket::is_exception(int sec, int usec) const { FdSet rfds; FdSet tfds = fds(); select_again: int n = tfds.select(NULL, NULL, &rfds, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; } #endif