2 * Copyright 2001-2005 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * shib-sock.cpp -- Socket-based IListener implementations
20 * Created by: Derek Atkins <derek@ihtfp.com>, revised by Scott Cantor
23 #include <saml/saml.h> // need this to "prime" the xmlsec-constrained windows.h declaration
24 #include <shib-target/shibrpc.h>
28 # include <sys/socket.h>
31 # include <arpa/inet.h>
34 #include <sys/types.h>
35 #include <sys/stat.h> /* for chmod() */
42 using namespace shibboleth;
43 using namespace shibtarget;
44 using namespace shibtarget::logging;
46 static const XMLCh address[] = { chLatin_a, chLatin_d, chLatin_d, chLatin_r, chLatin_e, chLatin_s, chLatin_s, chNull };
47 static const XMLCh port[] = { chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
49 class TCPListener : virtual public RPCListener
52 TCPListener(const DOMElement* e);
55 bool create(ShibSocket& s) const;
56 bool bind(ShibSocket& s, bool force=false) const;
57 bool connect(ShibSocket& s) const;
58 bool close(ShibSocket& s) const;
59 bool accept(ShibSocket& listener, ShibSocket& s) const;
60 void* getClientHandle(ShibSocket& s, u_long program, u_long version) const;
63 void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
64 bool log_error() const;
67 unsigned short m_port;
71 IPlugIn* TCPListenerFactory(const DOMElement* e)
73 return new TCPListener(e);
76 TCPListener::TCPListener(const DOMElement* e) : RPCListener(e), m_address("127.0.0.1"), m_port(12345)
78 // We're stateless, but we need to load the configuration.
79 const XMLCh* tag=e->getAttributeNS(NULL,address);
85 tag=e->getAttributeNS(NULL,port);
87 m_port=XMLString::parseInt(tag);
92 tag=e->getAttributeNS(NULL,SHIBT_L(acl));
94 auto_ptr_char temp(tag);
95 string sockacl=temp.get();
96 if (sockacl.length()) {
98 for (unsigned int i=0; i < sockacl.length(); i++) {
99 if (sockacl.at(i)==' ') {
100 m_acl.push_back(sockacl.substr(j, i-j));
104 m_acl.push_back(sockacl.substr(j, sockacl.length()-j));
108 m_acl.push_back("127.0.0.1");
111 void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const
113 // Split on host:port boundary. Default to port only.
114 memset(addr,0,sizeof(struct sockaddr_in));
115 addr->sin_family=AF_INET;
116 addr->sin_port=htons(m_port);
117 addr->sin_addr.s_addr=inet_addr(m_address.c_str());
120 bool TCPListener::log_error() const
123 int rc=WSAGetLastError();
127 #ifdef HAVE_STRERROR_R
129 memset(buf,0,sizeof(buf));
130 strerror_r(rc,buf,sizeof(buf));
131 log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
133 const char* buf=strerror(rc);
134 log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
139 bool TCPListener::create(ShibSocket& s) const
141 s=socket(AF_INET,SOCK_STREAM,0);
143 if(s==INVALID_SOCKET)
151 bool TCPListener::bind(ShibSocket& s, bool force) const
153 struct sockaddr_in addr;
154 setup_tcp_sockaddr(&addr);
156 // XXX: Do we care about the return value from setsockopt?
158 ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
161 if (SOCKET_ERROR==::bind(s,(struct sockaddr *)&addr,sizeof(addr)) || SOCKET_ERROR==::listen(s,3)) {
167 if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
177 bool TCPListener::connect(ShibSocket& s) const
179 struct sockaddr_in addr;
180 setup_tcp_sockaddr(&addr);
182 if(SOCKET_ERROR==::connect(s,(struct sockaddr *)&addr,sizeof(addr)))
185 if (::connect(s, (struct sockaddr*)&addr, sizeof (addr)) < 0)
191 bool TCPListener::close(ShibSocket& s) const
201 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
203 struct sockaddr_in addr;
206 int size=sizeof(addr);
207 s=::accept(listener,(struct sockaddr*)&addr,&size);
208 if(s==INVALID_SOCKET)
210 socklen_t size=sizeof(addr);
211 s=::accept(listener,(struct sockaddr*)&addr,&size);
215 char* client=inet_ntoa(addr.sin_addr);
216 for (vector<string>::const_iterator i=m_acl.begin(); i!=m_acl.end(); i++) {
222 log->error("accept() rejected client at %s\n",client);
226 void* TCPListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
228 struct sockaddr_in sin;
229 memset (&sin, 0, sizeof (sin));
231 return clnttcp_create(&sin, program, version, &s, 0, 0);
236 class UnixListener : virtual public RPCListener
239 UnixListener(const DOMElement* e);
240 ~UnixListener() {if (m_bound) unlink(m_address.c_str());}
242 bool create(ShibSocket& s) const;
243 bool bind(ShibSocket& s, bool force=false) const;
244 bool connect(ShibSocket& s) const;
245 bool close(ShibSocket& s) const;
246 bool accept(ShibSocket& listener, ShibSocket& s) const;
247 void* getClientHandle(ShibSocket& s, u_long program, u_long version) const;
250 bool log_error() const;
253 mutable bool m_bound;
256 IPlugIn* UnixListenerFactory(const DOMElement* e)
258 return new UnixListener(e);
261 UnixListener::UnixListener(const DOMElement* e) : RPCListener(e), m_address("/var/run/shar-socket"), m_bound(false)
263 // We're stateless, but we need to load the configuration.
264 const XMLCh* tag=e->getAttributeNS(NULL,address);
266 auto_ptr_char a(tag);
271 bool UnixListener::log_error() const
274 #ifdef HAVE_STRERROR_R
276 memset(buf,0,sizeof(buf));
277 strerror_r(rc,buf,sizeof(buf));
278 log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
280 const char* buf=strerror(rc);
281 log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
286 #ifndef UNIX_PATH_MAX
287 #define UNIX_PATH_MAX 100
290 bool UnixListener::create(ShibSocket& sock) const
292 sock = socket(PF_UNIX, SOCK_STREAM, 0);
298 bool UnixListener::bind(ShibSocket& s, bool force) const
300 struct sockaddr_un addr;
301 memset(&addr, 0, sizeof (addr));
302 addr.sun_family = AF_UNIX;
303 strncpy(addr.sun_path, m_address.c_str(), UNIX_PATH_MAX);
306 unlink(m_address.c_str());
308 if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
314 /* Make sure that only the creator can read -- we don't want just
315 * anyone connecting, do we?
317 if (chmod(m_address.c_str(),0777) < 0) {
320 unlink(m_address.c_str());
328 bool UnixListener::connect(ShibSocket& s) const
330 struct sockaddr_un addr;
331 memset(&addr, 0, sizeof (addr));
332 addr.sun_family = AF_UNIX;
333 strncpy(addr.sun_path, m_address.c_str(), UNIX_PATH_MAX);
335 if (::connect(s, (struct sockaddr *)&addr, sizeof (addr)) < 0)
340 bool UnixListener::close(ShibSocket& s) const
346 bool UnixListener::accept(ShibSocket& listener, ShibSocket& s) const
348 s=::accept(listener,NULL,NULL);
354 void* UnixListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
356 struct sockaddr_in sin;
357 memset (&sin, 0, sizeof (sin));
359 return clnttcp_create(&sin, program, version, &s, 0, 0);