Set SO_REUSEADDR on the tcp listener.
[shibboleth/cpp-sp.git] / shib-target / shib-sock.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution, if any, must include
17  * the following acknowledgment: "This product includes software developed by
18  * the University Corporation for Advanced Internet Development
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20  * may appear in the software itself, if and wherever such third-party
21  * acknowledgments normally appear.
22  *
23  * Neither the name of Shibboleth nor the names of its contributors, nor
24  * Internet2, nor the University Corporation for Advanced Internet Development,
25  * Inc., nor UCAID may be used to endorse or promote products derived from this
26  * software without specific prior written permission. For written permission,
27  * please contact shibboleth@shibboleth.org
28  *
29  * Products derived from this software may not be called Shibboleth, Internet2,
30  * UCAID, or the University Corporation for Advanced Internet Development, nor
31  * may Shibboleth appear in their name, without prior written permission of the
32  * University Corporation for Advanced Internet Development.
33  *
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /*
51  * shib-sock.cpp -- Socket-based IListener implementations
52  *
53  * Created by:  Derek Atkins <derek@ihtfp.com>, revised by Scott Cantor
54  */
55
56 #include "internal.h"
57
58 #ifdef HAVE_UNISTD_H
59 # include <sys/socket.h>
60 # include <sys/un.h>
61 # include <unistd.h>
62 # include <arpa/inet.h>
63 #else
64 # include <winsock.h>
65 #endif
66
67 #include <sys/types.h>
68 #include <sys/stat.h>           /* for chmod() */
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <errno.h>
72
73 using namespace std;
74 using namespace saml;
75 using namespace shibboleth;
76 using namespace shibtarget;
77 using namespace log4cpp;
78
79 static const XMLCh address[] = { chLatin_a, chLatin_d, chLatin_d, chLatin_r, chLatin_e, chLatin_s, chLatin_s, chNull };
80 static const XMLCh port[] = { chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
81 static const XMLCh acl[] = { chLatin_a, chLatin_c, chLatin_l, chNull };
82
83 class TCPListener : public IListener
84 {
85 public:
86     TCPListener(const DOMElement* e);
87     ~TCPListener() {}
88
89     bool create(ShibSocket& s) const;
90     bool bind(ShibSocket& s, bool force=false) const;
91     bool connect(ShibSocket& s) const;
92     bool close(ShibSocket& s) const;
93     bool accept(ShibSocket& listener, ShibSocket& s) const;
94     CLIENT* getClientHandle(ShibSocket& s, u_long program, u_long version) const;
95     
96 private:
97     void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
98     bool log_error() const;
99
100     string m_address;
101     unsigned short m_port;
102     vector<string> m_acl;
103     Category* m_log;
104 };
105
106 IPlugIn* TCPListenerFactory(const DOMElement* e)
107 {
108     return new TCPListener(e);
109 }
110
111 TCPListener::TCPListener(const DOMElement* e) : m_address("127.0.0.1"), m_port(12345),
112     m_log(&Category::getInstance("shibtarget.TCPListener"))
113 {
114     // We're stateless, but we need to load the configuration.
115     const XMLCh* tag=e->getAttributeNS(NULL,address);
116     if (tag && *tag) {
117         auto_ptr_char a(tag);
118         m_address=a.get();
119     }
120     
121     tag=e->getAttributeNS(NULL,port);
122     if (tag && *tag) {
123         m_port=XMLString::parseInt(tag);
124         if (m_port==0)
125             m_port=12345;
126     }
127     
128     tag=e->getAttributeNS(NULL,acl);
129     if (tag && *tag) {
130         auto_ptr_char temp(tag);
131         string sockacl=temp.get();
132         if (sockacl.length()) {
133             int j = 0;
134             for (int i=0;  i < sockacl.length();  i++) {
135                 if (sockacl.at(i)==' ') {
136                     m_acl.push_back(sockacl.substr(j, i-j));
137                     j = i+1;
138                 }
139             }
140             m_acl.push_back(sockacl.substr(j, sockacl.length()-j));
141         }
142     }
143     else
144         m_acl.push_back("127.0.0.1");
145 }
146
147 void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const
148 {
149     // Split on host:port boundary. Default to port only.
150     memset(addr,0,sizeof(struct sockaddr_in));
151     addr->sin_family=AF_INET;
152     addr->sin_port=htons(m_port);
153     addr->sin_addr.s_addr=inet_addr(m_address.c_str());
154 }
155
156 bool TCPListener::log_error() const
157 {
158 #ifdef WIN32
159     int rc=WSAGetLastError();
160 #else
161     int rc=errno;
162 #endif
163 #ifdef HAVE_STRERROR_R
164     char buf[256];
165     strerror_r(rc,buf,sizeof(buf));
166     buf[255]=0;
167     m_log->error("socket call resulted in error (%d): %s",rc,buf);
168 #else
169     m_log->error("socket call resulted in error (%d): %s",rc,strerror(rc));
170 #endif
171     return false;
172 }
173
174 bool TCPListener::create(ShibSocket& s) const
175 {
176     s=socket(AF_INET,SOCK_STREAM,0);
177 #ifdef WIN32
178     if(s==INVALID_SOCKET)
179 #else
180     if (s < 0)
181 #endif
182         return log_error();
183     return true;
184 }
185
186 bool TCPListener::bind(ShibSocket& s, bool force) const
187 {
188     struct sockaddr_in addr;
189     setup_tcp_sockaddr(&addr);
190 #ifdef WIN32
191     if (SOCKET_ERROR==::bind(s,(struct sockaddr *)&addr,sizeof(addr)) || SOCKET_ERROR==::listen(s,3)) {
192         log_error();
193         close(s);
194         return false;
195     }
196 #else
197     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
198         log_error();
199         close(s);
200         return false;
201     }
202     // XXX: Do we care about the return value from setsockopt?
203     int opt = 1;
204     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
205     ::listen(s,3);
206 #endif
207     return true;
208 }
209
210 bool TCPListener::connect(ShibSocket& s) const
211 {
212     struct sockaddr_in addr;
213     setup_tcp_sockaddr(&addr);
214 #ifdef WIN32
215     if(SOCKET_ERROR==::connect(s,(struct sockaddr *)&addr,sizeof(addr)))
216         return log_error();
217 #else
218     if (::connect(s, (struct sockaddr*)&addr, sizeof (addr)) < 0)
219         return log_error();
220 #endif
221     return true;
222 }
223
224 bool TCPListener::close(ShibSocket& s) const
225 {
226 #ifdef WIN32
227     closesocket(s);
228 #else
229     ::close(s);
230 #endif
231     return true;
232 }
233
234 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
235 {
236     struct sockaddr_in addr;
237
238 #ifdef WIN32
239     int size=sizeof(addr);
240     s=::accept(listener,(struct sockaddr*)&addr,&size);
241     if(s==INVALID_SOCKET)
242 #else
243     socklen_t size=sizeof(addr);
244     s=::accept(listener,(struct sockaddr*)&addr,&size);
245     if (s < 0)
246 #endif
247         return log_error();
248     char* client=inet_ntoa(addr.sin_addr);
249     for (vector<string>::const_iterator i=m_acl.begin(); i!=m_acl.end(); i++) {
250         if (*i==client)
251             return true;
252     }
253     close(s);
254     s=-1;
255     m_log->error("accept() rejected client at %s\n",client);
256     return false;
257 }
258
259 CLIENT* TCPListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
260 {
261     struct sockaddr_in sin;
262     memset (&sin, 0, sizeof (sin));
263     sin.sin_port = 1;
264     return clnttcp_create(&sin, program, version, &s, 0, 0);
265 }
266
267 #ifndef WIN32
268
269 class UnixListener : public IListener
270 {
271 public:
272     UnixListener(const DOMElement* e);
273     ~UnixListener() {if (m_bound) unlink(m_address.c_str());}
274
275     bool create(ShibSocket& s) const;
276     bool bind(ShibSocket& s, bool force=false) const;
277     bool connect(ShibSocket& s) const;
278     bool close(ShibSocket& s) const;
279     bool accept(ShibSocket& listener, ShibSocket& s) const;
280     CLIENT* getClientHandle(ShibSocket& s, u_long program, u_long version) const;
281     
282 private:
283     bool log_error() const;
284
285     string m_address;
286     mutable bool m_bound;
287     Category& m_log;
288 };
289
290 IPlugIn* UnixListenerFactory(const DOMElement* e)
291 {
292     return new UnixListener(e);
293 }
294
295 UnixListener::UnixListener(const DOMElement* e) : m_address("/tmp/shar-socket"), m_bound(false),
296     m_log(Category::getInstance("shibtarget.UnixListener"))
297 {
298     // We're stateless, but we need to load the configuration.
299     const XMLCh* tag=e->getAttributeNS(NULL,address);
300     if (tag && *tag) {
301         auto_ptr_char a(tag);
302         m_address=a.get();
303     }
304 }
305
306 bool UnixListener::log_error() const
307 {
308     int rc=errno;
309 #ifdef HAVE_STRERROR_R
310     char buf[256];
311     strerror_r(rc,buf,sizeof(buf));
312     buf[255]=0;
313     m_log.error("socket call resulted in error (%d): %s",rc,buf);
314 #else
315     m_log.error("socket call resulted in error (%d): %s",rc,strerror(rc));
316 #endif
317     return false;
318 }
319
320 #ifndef UNIX_PATH_MAX
321 #define UNIX_PATH_MAX 100
322 #endif
323
324 bool UnixListener::create(ShibSocket& sock) const
325 {
326     sock = socket(PF_UNIX, SOCK_STREAM, 0);
327     if (sock < 0)
328         return log_error();
329     return true;
330 }
331
332 bool UnixListener::bind(ShibSocket& s, bool force) const
333 {
334     struct sockaddr_un addr;
335     memset(&addr, 0, sizeof (addr));
336     addr.sun_family = AF_UNIX;
337     strncpy(addr.sun_path, m_address.c_str(), UNIX_PATH_MAX);
338
339     if (force)
340         unlink(m_address.c_str());
341
342     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
343         log_error();
344         close(s);
345         return false;
346     }
347
348     /* Make sure that only the creator can read -- we don't want just
349      * anyone connecting, do we?
350      */
351     if (chmod(m_address.c_str(),0777) < 0) {
352         log_error();
353         close(s);
354         unlink(m_address.c_str());
355         return false;
356     }
357
358     listen(s, 3);
359     return m_bound=true;
360 }
361
362 bool UnixListener::connect(ShibSocket& s) const
363 {
364     struct sockaddr_un addr;
365     memset(&addr, 0, sizeof (addr));
366     addr.sun_family = AF_UNIX;
367     strncpy(addr.sun_path, m_address.c_str(), UNIX_PATH_MAX);
368
369     if (::connect(s, (struct sockaddr *)&addr, sizeof (addr)) < 0)
370         return log_error();
371     return true;
372 }
373
374 bool UnixListener::close(ShibSocket& s) const
375 {
376     ::close(s);
377     return true;
378 }
379
380 bool UnixListener::accept(ShibSocket& listener, ShibSocket& s) const
381 {
382     s=::accept(listener,NULL,NULL);
383     if (s < 0)
384         return log_error();
385     return true;
386 }
387
388 CLIENT* UnixListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
389 {
390     struct sockaddr_in sin;
391     memset (&sin, 0, sizeof (sin));
392     sin.sin_port = 1;
393     return clnttcp_create(&sin, program, version, &s, 0, 0);
394 }
395
396 #endif /* !WIN32 */