/* * sockprot.cpp - protocol-independant socket addresses handling * $Id: sockprot.cpp,v 1.4 2004/07/23 14:48:39 rdenisc Exp $ */ /*********************************************************************** * Copyright (C) 2002-2004 Remi Denis-Courmont. * * This program is free software; you can redistribute and/or modify * * it under the terms of the GNU General Public License as published * * by the Free Software Foundation; version 2 of the license. * * * * 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, you can get it from: * * http://www.gnu.org/copyleft/gpl.html * ***********************************************************************/ #if HAVE_CONFIG_H # include #endif #include "gettext.h" #include "secstr.h" // secure_strncpy() #include #if HAVE_SYS_SOCKET_H # include #endif #if HAVE_SYS_UN_H # include // struct sockaddr_un #endif #include // errno #include // close(), unlink() #include // fcntl() #if HAVE_SYS_SELECT_H # include // select() #endif #include "solve.h" #include "sockprot.h" /*** SocketAddress class implementation ***/ int SocketAddress::SetFromSocket (int fd, int flags, int side) { if (res != NULL) freeai (res); ClearError (); struct sockaddr_storage addr; socklen_t len = sizeof (struct sockaddr_storage); if ((side) ? getpeername (fd, (struct sockaddr *)&addr, &len) : getsockname (fd, (struct sockaddr *)&addr, &len)) { res = NULL; return SetError (); } res = makeai ((struct sockaddr *)&addr, len); if (res == NULL) return SetError (); if (SetError (getnamebyaddr ((struct sockaddr *)res->ai_addr, res->ai_addrlen, myhost, sizeof (myhost), myserv, sizeof (myserv), flags))) { secure_strncpy (myhost, _("unknown_node"), sizeof (myhost)); secure_strncpy (myserv, _("unknown_service"), sizeof (myserv)); return GetError (); } return 0; } int SocketAddress::SetByName (const char *host, const char *service, int flags, int af, int type, int proto) { if (res != NULL) freeai (res); ClearError (); if (host != NULL) strncpy (myhost, host, sizeof (myhost)); else *myhost = 0; secure_strncpy (myserv, (service != NULL) ? service : "0", sizeof (myserv)); struct addrinfo info; memset (&info, 0, sizeof (info)); if (flags) info.ai_flags = flags; if (af) info.ai_family = af; if (type) info.ai_socktype = type; if (proto) info.ai_protocol = proto; if (SetError (getaddrbyname (host, service, &info, &res))) { res = NULL; return GetError (); } //if ((flags & AI_CANONNAME) && (res != NULL)) // -- broken if multiple results // secure_strncpy (myhost, sizeof (myhost), res->ai_canonname); return 0; } SocketAddress::~SocketAddress (void) { if (res != NULL) freeai (res); } SocketAddress::SocketAddress (const SocketAddress& src) : err_ai (src.err_ai), err_sys (src.err_sys) { secure_strncpy (myhost, src.myhost, sizeof (myhost)); secure_strncpy (myserv, src.myserv, sizeof (myserv)); if (src.res != NULL) { res = copyai (src.res); if (res == NULL) SetError (); } else res = NULL; } int SocketAddress::Bind (void) { for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) { int fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (fd != -1) { int t = 1; setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &t, sizeof (t)); if (bind (fd, ai->ai_addr, ai->ai_addrlen)) { SetError (); close (fd); } else { ClearError (); return fd; // success! } } else SetError (); } return -1; } const char * SocketAddress::StrError (void) const { return (err_ai == EAI_SYSTEM) ? strerror (err_sys) : gai_strerror (err_ai); } int SocketAddress::SetError (int ai) { if (ai == EAI_SYSTEM) err_sys = errno; return err_ai = ai; } int SocketAddress::Connect (int nonblock) { for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) { int fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol), t = 1; setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &t, sizeof (t)); if (fd != -1) { #ifdef O_NONBLOCK int flags = fcntl (fd, F_GETFL); fcntl (fd, F_SETFL, O_NONBLOCK); #endif if (connect (fd, ai->ai_addr, ai->ai_addrlen) == 0) { ClearError (); #ifdef O_NONBLOCK if (flags != -1) fcntl (fd, F_SETFL, flags); #endif return fd; } if (errno != EINPROGRESS) { SetError (); close (fd); continue; } if (nonblock) return fd; /* Waits until connection is established */ fd_set s; FD_ZERO (&s); FD_SET (fd, &s); int err = 0; socklen_t len = sizeof (err); if (select (fd + 1, NULL, &s, NULL, NULL) != 1) { SetError (); close (fd); if (err_sys == EINTR) // aborts if interrupted return -1; continue; } if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &err, &len)) { SetError (); close (fd); continue; } if (len != sizeof (err)) continue; // impossible error if (err) { errno = err; SetError (); close (fd); continue; } ClearError (); #ifdef O_NONBLOCK if (flags != -1) fcntl (fd, F_SETFL, flags); #endif return fd; } } return -1; } void SocketAddress::CleanUp (void) const { #if HAVE_SYS_UN_H for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) if (ai->ai_family == PF_LOCAL) unlink (((const struct sockaddr_un *)ai->ai_addr)->sun_path); #endif }