/* * 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 "TCPSocket.h" #include #include #include #include #include #include #ifdef PROTOGEN # include "exception.h" #else # include # 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 #include /********************************************************************** * address resolving **********************************************************************/ void TCPSocket::parse_args(const char *s, int l) { if(l == -1) l = strlen(s); const char *host_p = NULL; int host_l = 0; const char *serv_p = NULL; int serv_l = 0; if(l == 0) goto finish; while(l > 0 && isspace(*s)) { s++; l--; } host_p = s; while(l > 0 && *s != ':') { s++; l--; host_l++; } if(l == 0) { // if there is only one argument it is the service, not the hostname serv_p = host_p; serv_l = host_l; host_p = NULL; host_l = 0; goto finish; } else { s++; l--; } while(l > 0 && isspace(*s)) { s++; l--; } serv_p = s; while(l > 0 && isalnum(*s)) { s++; l--; serv_l++; } finish: char hostb[host_l + 1]; char servb[serv_l + 1]; char *host = NULL; char *serv = NULL; if(host_p != NULL) { // remove trailing spaces from hostname const char *s = host_p + host_l; while(host_l > 0 && isspace(*--s)) { host_l--; } memcpy(hostb, host_p, host_l); hostb[host_l] = 0; host=hostb; } if(serv_p != NULL) { // remove trailing spaces from servname const char *s = serv_p + serv_l; while(serv_l > 0 && isspace(*--s)) { serv_l--; } memcpy(servb, serv_p, serv_l); servb[serv_l] = 0; serv=servb; } setaddr(serv, host); } void TCPSocket::setaddr(const char *serv, const char *host) // set the address by host and service { bzero(&_saddr, sizeof(_saddr)); if(host != NULL) { struct hostent *hostp = gethostbyname(host); if (hostp == NULL) ERROR("%s: host not found", host); memcpy(&_saddr.sin_addr, hostp->h_addr, hostp->h_length); _saddr.sin_family = hostp->h_addrtype; } if(serv == NULL) ERROR("missing service name"); else { struct servent *servp = getservbyname(serv, "tcp"); if(servp == NULL) ERROS("%s: service not found", serv); _saddr.sin_port = servp->s_port; } } /********************************************************************** * ctor, dtor, and copy methods **********************************************************************/ TCPSocket::TCPSocket(const char *, const char *param, size_t parlen) : Socket(), _sigpipe(SIGPIPE) { _sd = -1; _sigpipe.ignore(); _state_plus |= accept_able; parse_args(param, parlen); // sets saddr; bzero(&_paddr, sizeof(_paddr)); } TCPSocket::TCPSocket() : Socket(), _sigpipe(SIGPIPE) { _sd = -1; _sigpipe.ignore(); bzero(&_saddr, sizeof(_saddr)); bzero(&_paddr, sizeof(_paddr)); } TCPSocket::~TCPSocket() { if(_sd != -1) { ::close(_sd); } } Socket *TCPSocket::ctor(const char *address, const char *param, size_t parlen) { return new TCPSocket(address, param, parlen); } Socket *TCPSocket::ctor() { return new TCPSocket; } /********************************************************************** * socket methods **********************************************************************/ bool TCPSocket::connect(flags_t mode) { if(_sd != -1) { ::close(_sd); } _sd = ::socket(AF_INET, SOCK_STREAM, 0); if(_sd == -1) ERROS("socket"); if(::connect(_sd, (struct sockaddr*)&_saddr, sizeof(_saddr)) == -1) ERROS("connect"); _state_plus |= connected | send_recv & ~eox_any; if(!(mode & send_mode)) shutdown(send_mode); if(!(mode & recv_mode)) shutdown(recv_mode); return true; } Socket *TCPSocket::accept(flags_t mode) { if(! (_state_plus & accept_able)) ERROR("illegal operation"); if(_sd == -1) { _sd = socket(AF_INET, SOCK_STREAM, 0); if(_sd == -1) ERROS("socket"); if(::bind(_sd, (struct sockaddr *)&_saddr, sizeof(_saddr)) == -1) ERROS("bind"); if(::listen(_sd, 5) == -1) ERROS("listen"); } int sd; struct sockaddr_in paddr; int len = sizeof(paddr); bzero(&paddr, len); sd = ::accept(_sd, (struct sockaddr *)&paddr, &len); if(sd == -1) ERROS("accept"); TCPSocket *ps = new TCPSocket(); ps->_sd = sd; ps->_paddr = paddr; ps->_saddr = _saddr; ps->_state_plus = _state_plus & ~accept_able | connected | send_recv & ~eox_any; if(!(mode & send_mode)) ps->shutdown(send_mode); if(!(mode & recv_mode)) ps->shutdown(recv_mode); return ps; } size_t TCPSocket::send(const char *buf, size_t len, int msgf) { size_t n = 0; // filter out the cases where SIGPIPE wouldn't be appropriate if(! ((len == 0) && (msgf & (msg_eot | msg_eom)))) n = ::send(_sd, buf, len, msgf & ~(msg_eot | msg_eom)); if(n == (size_t)-1) ERROS("send failed"); if( msgf & ( msg_eom | msg_eom ) ) { if( msgf & msg_eom ) { // never shut down write end twice, since this can close the read end! if(! ( _state_plus & ( eom_placed | eot_placed ) ) ) shutdown(send_mode); _state_plus |= eom_placed; } if( msgf & msg_eot ) { // never shut down write end twice, since this can close the read end! if(! ( _state_plus & ( eom_placed | eot_placed ) ) ) shutdown(send_mode); _state_plus |= eot_placed; } } return n; } size_t TCPSocket::recv(char *buf, size_t max, int msgf) { size_t n = ::recv(_sd, buf, max, msgf); if(n == (size_t)-1) ERROS("recv failed"); if( n == 0 ) _state_plus |= eom_seen | eot_seen; return n; } void TCPSocket::shutdown(flags_t mode) { if(mode & recv_mode) ::shutdown(_sd, 0); else if(mode & send_mode) ::shutdown(_sd, 1); else ::shutdown(_sd, 2); _state_plus = _state_plus & ~( mode & send_recv ); } void TCPSocket::close(flags_t) { if(_sd != -1) { ::close(_sd); _sd = -1; } _state_plus &= ~( connected | send_recv ); } bool TCPSocket::async(bool on, pid_t pid) { bool old = _state_plus & async_mode; int flags; if(fcntl(_sd, F_GETFL, &flags) == -1) ERROS("fcntl:F_GETFL:"); if(on) { if(pid != 0) if(fcntl(_sd, F_SETOWN, &pid) == -1) ERROS("fcntl:F_SETOWN:"); flags |= O_ASYNC; _state_plus |= async_mode; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); } else // off { flags &= ~O_ASYNC; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); _state_plus &= ~async_mode; } return old; } bool TCPSocket::noblock(bool on) { bool old = _state_plus & noblock_mode; int flags; if(fcntl(_sd, F_GETFL, &flags) == -1) ERROS("fcntl:F_GETFL:"); if(on) { flags |= O_NONBLOCK; _state_plus |= noblock_mode; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETOWN:"); } else // off { flags &= ~O_NONBLOCK; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); _state_plus &= ~noblock_mode; } return old; } bool TCPSocket::is_sendready(int sec, int usec) const { FdSet rfds; FdSet tfds(_sd, -1); 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 TCPSocket::is_recvready(int sec, int usec) const { FdSet rfds; FdSet tfds(_sd, -1); 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 TCPSocket::is_exception(int sec, int usec) const { FdSet rfds; FdSet tfds(_sd, -1); 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; }