https://issues.shibboleth.net/jira/browse/SSPCPP-69
[shibboleth/cpp-sp.git] / shib-target / shib-sock.cpp
1 /*
2  *  Copyright 2001-2005 Internet2
3  * 
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * shib-sock.cpp -- Socket-based IListener implementations
19  *
20  * Created by:  Derek Atkins <derek@ihtfp.com>, revised by Scott Cantor
21  */
22
23 #include <saml/saml.h>  // need this to "prime" the xmlsec-constrained windows.h declaration
24 #include <shib-target/shibrpc.h>
25 #include "internal.h"
26
27 #ifdef HAVE_UNISTD_H
28 # include <sys/socket.h>
29 # include <sys/un.h>
30 # include <unistd.h>
31 # include <arpa/inet.h>
32 #endif
33
34 #include <sys/types.h>
35 #include <sys/stat.h>           /* for chmod() */
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <errno.h>
39
40 using namespace std;
41 using namespace saml;
42 using namespace shibboleth;
43 using namespace shibtarget;
44 using namespace shibtarget::logging;
45
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 };
48
49 class TCPListener : virtual public RPCListener
50 {
51 public:
52     TCPListener(const DOMElement* e);
53     ~TCPListener() {}
54
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;
61     
62 private:
63     void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
64     bool log_error() const;
65
66     string m_address;
67     unsigned short m_port;
68     vector<string> m_acl;
69 };
70
71 IPlugIn* TCPListenerFactory(const DOMElement* e)
72 {
73     return new TCPListener(e);
74 }
75
76 TCPListener::TCPListener(const DOMElement* e) : RPCListener(e), m_address("127.0.0.1"), m_port(12345)
77 {
78     // We're stateless, but we need to load the configuration.
79     const XMLCh* tag=e->getAttributeNS(NULL,address);
80     if (tag && *tag) {
81         auto_ptr_char a(tag);
82         m_address=a.get();
83     }
84     
85     tag=e->getAttributeNS(NULL,port);
86     if (tag && *tag) {
87         m_port=XMLString::parseInt(tag);
88         if (m_port==0)
89             m_port=12345;
90     }
91     
92     tag=e->getAttributeNS(NULL,SHIBT_L(acl));
93     if (tag && *tag) {
94         auto_ptr_char temp(tag);
95         string sockacl=temp.get();
96         if (sockacl.length()) {
97             unsigned int j = 0;
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));
101                     j = i+1;
102                 }
103             }
104             m_acl.push_back(sockacl.substr(j, sockacl.length()-j));
105         }
106     }
107     else
108         m_acl.push_back("127.0.0.1");
109 }
110
111 void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const
112 {
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());
118 }
119
120 bool TCPListener::log_error() const
121 {
122 #ifdef WIN32
123     int rc=WSAGetLastError();
124 #else
125     int rc=errno;
126 #endif
127 #ifdef HAVE_STRERROR_R
128     char buf[256];
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");
132 #else
133     const char* buf=strerror(rc);
134     log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
135 #endif
136     return false;
137 }
138
139 bool TCPListener::create(ShibSocket& s) const
140 {
141     s=socket(AF_INET,SOCK_STREAM,0);
142 #ifdef WIN32
143     if(s==INVALID_SOCKET)
144 #else
145     if (s < 0)
146 #endif
147         return log_error();
148     return true;
149 }
150
151 bool TCPListener::bind(ShibSocket& s, bool force) const
152 {
153     struct sockaddr_in addr;
154     setup_tcp_sockaddr(&addr);
155
156     // XXX: Do we care about the return value from setsockopt?
157     int opt = 1;
158     ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
159
160 #ifdef WIN32
161     if (SOCKET_ERROR==::bind(s,(struct sockaddr *)&addr,sizeof(addr)) || SOCKET_ERROR==::listen(s,3)) {
162         log_error();
163         close(s);
164         return false;
165     }
166 #else
167     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
168         log_error();
169         close(s);
170         return false;
171     }
172     ::listen(s,3);
173 #endif
174     return true;
175 }
176
177 bool TCPListener::connect(ShibSocket& s) const
178 {
179     struct sockaddr_in addr;
180     setup_tcp_sockaddr(&addr);
181 #ifdef WIN32
182     if(SOCKET_ERROR==::connect(s,(struct sockaddr *)&addr,sizeof(addr)))
183         return log_error();
184 #else
185     if (::connect(s, (struct sockaddr*)&addr, sizeof (addr)) < 0)
186         return log_error();
187 #endif
188     return true;
189 }
190
191 bool TCPListener::close(ShibSocket& s) const
192 {
193 #ifdef WIN32
194     closesocket(s);
195 #else
196     ::close(s);
197 #endif
198     return true;
199 }
200
201 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
202 {
203     struct sockaddr_in addr;
204
205 #ifdef WIN32
206     int size=sizeof(addr);
207     s=::accept(listener,(struct sockaddr*)&addr,&size);
208     if(s==INVALID_SOCKET)
209 #else
210     socklen_t size=sizeof(addr);
211     s=::accept(listener,(struct sockaddr*)&addr,&size);
212     if (s < 0)
213 #endif
214         return log_error();
215     char* client=inet_ntoa(addr.sin_addr);
216     for (vector<string>::const_iterator i=m_acl.begin(); i!=m_acl.end(); i++) {
217         if (*i==client)
218             return true;
219     }
220     close(s);
221     s=-1;
222     log->error("accept() rejected client at %s\n",client);
223     return false;
224 }
225
226 void* TCPListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
227 {
228     struct sockaddr_in sin;
229     memset (&sin, 0, sizeof (sin));
230     sin.sin_port = 1;
231     return clnttcp_create(&sin, program, version, &s, 0, 0);
232 }
233
234 #ifndef WIN32
235
236 class UnixListener : virtual public RPCListener
237 {
238 public:
239     UnixListener(const DOMElement* e);
240     ~UnixListener() {if (m_bound) unlink(m_address.c_str());}
241
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;
248     
249 private:
250     bool log_error() const;
251
252     string m_address;
253     mutable bool m_bound;
254 };
255
256 IPlugIn* UnixListenerFactory(const DOMElement* e)
257 {
258     return new UnixListener(e);
259 }
260
261 UnixListener::UnixListener(const DOMElement* e) : RPCListener(e), m_address("/var/run/shar-socket"), m_bound(false)
262 {
263     // We're stateless, but we need to load the configuration.
264     const XMLCh* tag=e->getAttributeNS(NULL,address);
265     if (tag && *tag) {
266         auto_ptr_char a(tag);
267         m_address=a.get();
268     }
269 }
270
271 bool UnixListener::log_error() const
272 {
273     int rc=errno;
274 #ifdef HAVE_STRERROR_R
275     char buf[256];
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");
279 #else
280     const char* buf=strerror(rc);
281     log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
282 #endif
283     return false;
284 }
285
286 #ifndef UNIX_PATH_MAX
287 #define UNIX_PATH_MAX 100
288 #endif
289
290 bool UnixListener::create(ShibSocket& sock) const
291 {
292     sock = socket(PF_UNIX, SOCK_STREAM, 0);
293     if (sock < 0)
294         return log_error();
295     return true;
296 }
297
298 bool UnixListener::bind(ShibSocket& s, bool force) const
299 {
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);
304
305     if (force)
306         unlink(m_address.c_str());
307
308     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
309         log_error();
310         close(s);
311         return false;
312     }
313
314     /* Make sure that only the creator can read -- we don't want just
315      * anyone connecting, do we?
316      */
317     if (chmod(m_address.c_str(),0777) < 0) {
318         log_error();
319         close(s);
320         unlink(m_address.c_str());
321         return false;
322     }
323
324     listen(s, 3);
325     return m_bound=true;
326 }
327
328 bool UnixListener::connect(ShibSocket& s) const
329 {
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);
334
335     if (::connect(s, (struct sockaddr *)&addr, sizeof (addr)) < 0)
336         return log_error();
337     return true;
338 }
339
340 bool UnixListener::close(ShibSocket& s) const
341 {
342     ::close(s);
343     return true;
344 }
345
346 bool UnixListener::accept(ShibSocket& listener, ShibSocket& s) const
347 {
348     s=::accept(listener,NULL,NULL);
349     if (s < 0)
350         return log_error();
351     return true;
352 }
353
354 void* UnixListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
355 {
356     struct sockaddr_in sin;
357     memset (&sin, 0, sizeof (sin));
358     sin.sin_port = 1;
359     return clnttcp_create(&sin, program, version, &s, 0, 0);
360 }
361
362 #endif /* !WIN32 */