2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
24 * TCP-based SocketListener implementation.
28 #include "exceptions.h"
29 #include "remoting/impl/SocketListener.h"
30 #include "util/IPRange.h"
32 #include <boost/bind.hpp>
33 #include <boost/algorithm/string.hpp>
34 #include <xercesc/util/XMLUniDefs.hpp>
35 #include <xmltooling/unicode.h>
36 #include <xmltooling/util/XMLHelper.h>
39 # include <winsock2.h>
40 # include <ws2tcpip.h>
44 # include <sys/socket.h>
48 # include <arpa/inet.h>
49 # include <netinet/in.h>
52 #include <sys/types.h>
53 #include <sys/stat.h> /* for chmod() */
59 using namespace shibsp;
60 using namespace xmltooling;
61 using namespace xercesc;
62 using namespace boost;
66 class TCPListener : virtual public SocketListener
69 TCPListener(const DOMElement* e);
72 bool create(ShibSocket& s) const;
73 bool bind(ShibSocket& s, bool force=false) const;
74 bool connect(ShibSocket& s) const;
75 bool close(ShibSocket& s) const;
76 bool accept(ShibSocket& listener, ShibSocket& s) const;
78 int send(ShibSocket& s, const char* buf, int len) const {
79 return ::send(s, buf, len, 0);
82 int recv(ShibSocket& s, char* buf, int buflen) const {
83 return ::recv(s, buf, buflen, 0);
87 bool setup_tcp_sockaddr();
90 unsigned short m_port;
91 vector<IPRange> m_acl;
93 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE
94 struct sockaddr_storage m_sockaddr;
96 struct sockaddr_in m_sockaddr;
100 ListenerService* SHIBSP_DLLLOCAL TCPListenerServiceFactory(const DOMElement* const & e)
102 return new TCPListener(e);
105 static const XMLCh address[] = UNICODE_LITERAL_7(a,d,d,r,e,s,s);
106 static const XMLCh port[] = UNICODE_LITERAL_4(p,o,r,t);
107 static const XMLCh acl[] = UNICODE_LITERAL_3(a,c,l);
110 TCPListener::TCPListener(const DOMElement* e)
112 m_address(XMLHelper::getAttrString(e, getenv("SHIBSP_LISTENER_ADDRESS"), address)),
113 m_port(XMLHelper::getAttrInt(e, 0, port))
115 if (m_address.empty())
116 m_address = "127.0.0.1";
119 const char* p = getenv("SHIBSP_LISTENER_PORT");
126 vector<string> rawacls;
127 string aclbuf = XMLHelper::getAttrString(e, "127.0.0.1", acl);
128 boost::split(rawacls, aclbuf, boost::is_space(), algorithm::token_compress_on);
129 for (vector<string>::const_iterator i = rawacls.begin(); i < rawacls.end(); ++i) {
131 m_acl.push_back(IPRange::parseCIDRBlock(i->c_str()));
133 catch (std::exception& ex) {
134 log->error("invalid CIDR block (%s): %s", i->c_str(), ex.what());
139 log->warn("invalid CIDR range(s) in acl property, allowing 127.0.0.1 as a fall back");
140 m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
143 if (!setup_tcp_sockaddr()) {
144 throw ConfigurationException("Unable to use configured socket address property.");
148 bool TCPListener::setup_tcp_sockaddr()
150 struct addrinfo* ret = nullptr;
151 struct addrinfo hints;
153 memset(&hints, 0, sizeof(hints));
154 hints.ai_flags = AI_NUMERICHOST;
155 hints.ai_family = AF_UNSPEC;
157 if (getaddrinfo(m_address.c_str(), nullptr, &hints, &ret) != 0) {
158 log->error("unable to parse server address (%s)", m_address.c_str());
162 m_sockaddrlen = ret->ai_addrlen;
163 if (ret->ai_family == AF_INET) {
164 memcpy(&m_sockaddr, ret->ai_addr, m_sockaddrlen);
166 ((struct sockaddr_in*)&m_sockaddr)->sin_port=htons(m_port);
169 #if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_STORAGE)
170 else if (ret->ai_family == AF_INET6) {
171 memcpy(&m_sockaddr, ret->ai_addr, m_sockaddrlen);
173 ((struct sockaddr_in6*)&m_sockaddr)->sin6_port=htons(m_port);
178 log->error("unknown address type (%d)", ret->ai_family);
183 bool TCPListener::create(ShibSocket& s) const
185 int type = SOCK_STREAM;
186 #ifdef HAVE_SOCK_CLOEXEC
187 type |= SOCK_CLOEXEC;
190 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE
191 s = socket(m_sockaddr.ss_family, type, 0);
193 s = socket(m_sockaddr.sin_family, type, 0);
196 if(s == INVALID_SOCKET)
200 return log_error("socket");
202 #if !defined(HAVE_SOCK_CLOEXEC) && defined(HAVE_FD_CLOEXEC)
203 int fdflags = fcntl(s, F_GETFD);
205 fdflags |= FD_CLOEXEC;
206 fcntl(s, F_SETFD, fdflags);
213 bool TCPListener::bind(ShibSocket& s, bool force) const
215 // XXX: Do we care about the return value from setsockopt?
217 ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
220 if (SOCKET_ERROR==::bind(s, (const struct sockaddr*)&m_sockaddr, m_sockaddrlen) || SOCKET_ERROR==::listen(s, 3)) {
226 // Newer BSDs, and Solaris, require the struct length be passed based on the socket address.
227 // All but Solaris seem to have an ss_len field in the sockaddr_storage struct.
228 # ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
229 # ifdef HAVE_STRUCT_SOCKADDR_STORAGE
230 if (::bind(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.ss_len) < 0) {
232 if (::bind(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.sin_len) < 0) {
235 if (::bind(s, (const struct sockaddr*)&m_sockaddr, m_sockaddrlen) < 0) {
246 bool TCPListener::connect(ShibSocket& s) const
249 if(SOCKET_ERROR==::connect(s, (const struct sockaddr*)&m_sockaddr, m_sockaddrlen))
250 return log_error("connect");
252 // Newer BSDs require the struct length be passed based on the socket address.
253 // Others have no field for that and take the whole struct size like Windows does.
254 # ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
255 # ifdef HAVE_STRUCT_SOCKADDR_STORAGE
256 if (::connect(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.ss_len) < 0)
258 if (::connect(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.sin_len) < 0)
261 if (::connect(s, (const struct sockaddr*)&m_sockaddr, m_sockaddrlen) < 0)
263 return log_error("connect");
268 bool TCPListener::close(ShibSocket& s) const
278 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
280 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE
281 struct sockaddr_storage addr;
283 struct sockaddr_in addr;
285 memset(&addr, 0, sizeof(addr));
288 int size=sizeof(addr);
289 s=::accept(listener, (struct sockaddr*)&addr, &size);
290 if(s==INVALID_SOCKET)
292 socklen_t size=sizeof(addr);
293 s=::accept(listener, (struct sockaddr*)&addr, &size);
296 return log_error("accept");
298 static bool (IPRange::* contains)(const struct sockaddr*) const = &IPRange::contains;
299 if (find_if(m_acl.begin(), m_acl.end(), boost::bind(contains, _1, (const struct sockaddr*)&addr)) == m_acl.end()) {
302 log->error("accept() rejected client with invalid address");