512237b8150fd9e0984db030e334bb511fa2172d
[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 "RPCListener.h"
24
25 #ifdef HAVE_UNISTD_H
26 # include <sys/socket.h>
27 # include <sys/un.h>
28 # include <unistd.h>
29 # include <arpa/inet.h>
30 #endif
31
32 #include <sys/types.h>
33 #include <sys/stat.h>           /* for chmod() */
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <errno.h>
37
38 using namespace std;
39 using namespace saml;
40 using namespace shibboleth;
41 using namespace shibtarget;
42 using namespace log4cpp;
43
44 static const XMLCh address[] = { chLatin_a, chLatin_d, chLatin_d, chLatin_r, chLatin_e, chLatin_s, chLatin_s, chNull };
45 static const XMLCh port[] = { chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
46
47 class TCPListener : virtual public RPCListener
48 {
49 public:
50     TCPListener(const DOMElement* e);
51     ~TCPListener() {}
52
53     bool create(ShibSocket& s) const;
54     bool bind(ShibSocket& s, bool force=false) const;
55     bool connect(ShibSocket& s) const;
56     bool close(ShibSocket& s) const;
57     bool accept(ShibSocket& listener, ShibSocket& s) const;
58     CLIENT* getClientHandle(ShibSocket& s, u_long program, u_long version) const;
59     
60 private:
61     void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
62
63     string m_address;
64     unsigned short m_port;
65     vector<string> m_acl;
66 };
67
68 IPlugIn* TCPListenerFactory(const DOMElement* e)
69 {
70     return new TCPListener(e);
71 }
72
73 TCPListener::TCPListener(const DOMElement* e) : RPCListener(e), m_address("127.0.0.1"), m_port(12345)
74 {
75     // We're stateless, but we need to load the configuration.
76     const XMLCh* tag=e->getAttributeNS(NULL,address);
77     if (tag && *tag) {
78         auto_ptr_char a(tag);
79         m_address=a.get();
80     }
81     
82     tag=e->getAttributeNS(NULL,port);
83     if (tag && *tag) {
84         m_port=XMLString::parseInt(tag);
85         if (m_port==0)
86             m_port=12345;
87     }
88     
89     tag=e->getAttributeNS(NULL,SHIBT_L(acl));
90     if (tag && *tag) {
91         auto_ptr_char temp(tag);
92         string sockacl=temp.get();
93         if (sockacl.length()) {
94             int j = 0;
95             for (unsigned int i=0;  i < sockacl.length();  i++) {
96                 if (sockacl.at(i)==' ') {
97                     m_acl.push_back(sockacl.substr(j, i-j));
98                     j = i+1;
99                 }
100             }
101             m_acl.push_back(sockacl.substr(j, sockacl.length()-j));
102         }
103     }
104     else
105         m_acl.push_back("127.0.0.1");
106 }
107
108 void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const
109 {
110     // Split on host:port boundary. Default to port only.
111     memset(addr,0,sizeof(struct sockaddr_in));
112     addr->sin_family=AF_INET;
113     addr->sin_port=htons(m_port);
114     addr->sin_addr.s_addr=inet_addr(m_address.c_str());
115 }
116
117 bool TCPListener::create(ShibSocket& s) const
118 {
119     s=socket(AF_INET,SOCK_STREAM,0);
120 #ifdef WIN32
121     if(s==INVALID_SOCKET)
122 #else
123     if (s < 0)
124 #endif
125         return log_error();
126     return true;
127 }
128
129 bool TCPListener::bind(ShibSocket& s, bool force) const
130 {
131     struct sockaddr_in addr;
132     setup_tcp_sockaddr(&addr);
133
134     // XXX: Do we care about the return value from setsockopt?
135     int opt = 1;
136     ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
137
138 #ifdef WIN32
139     if (SOCKET_ERROR==::bind(s,(struct sockaddr *)&addr,sizeof(addr)) || SOCKET_ERROR==::listen(s,3)) {
140         log_error();
141         close(s);
142         return false;
143     }
144 #else
145     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
146         log_error();
147         close(s);
148         return false;
149     }
150     ::listen(s,3);
151 #endif
152     return true;
153 }
154
155 bool TCPListener::connect(ShibSocket& s) const
156 {
157     struct sockaddr_in addr;
158     setup_tcp_sockaddr(&addr);
159 #ifdef WIN32
160     if(SOCKET_ERROR==::connect(s,(struct sockaddr *)&addr,sizeof(addr)))
161         return log_error();
162 #else
163     if (::connect(s, (struct sockaddr*)&addr, sizeof (addr)) < 0)
164         return log_error();
165 #endif
166     return true;
167 }
168
169 bool TCPListener::close(ShibSocket& s) const
170 {
171 #ifdef WIN32
172     closesocket(s);
173 #else
174     ::close(s);
175 #endif
176     return true;
177 }
178
179 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
180 {
181     struct sockaddr_in addr;
182
183 #ifdef WIN32
184     int size=sizeof(addr);
185     s=::accept(listener,(struct sockaddr*)&addr,&size);
186     if(s==INVALID_SOCKET)
187 #else
188     socklen_t size=sizeof(addr);
189     s=::accept(listener,(struct sockaddr*)&addr,&size);
190     if (s < 0)
191 #endif
192         return log_error();
193     char* client=inet_ntoa(addr.sin_addr);
194     for (vector<string>::const_iterator i=m_acl.begin(); i!=m_acl.end(); i++) {
195         if (*i==client)
196             return true;
197     }
198     close(s);
199     s=-1;
200     log->error("accept() rejected client at %s\n",client);
201     return false;
202 }
203
204 CLIENT* TCPListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
205 {
206     struct sockaddr_in sin;
207     memset (&sin, 0, sizeof (sin));
208     sin.sin_port = 1;
209     return clnttcp_create(&sin, program, version, &s, 0, 0);
210 }
211
212 #ifndef WIN32
213
214 class UnixListener : virtual public RPCListener
215 {
216 public:
217     UnixListener(const DOMElement* e);
218     ~UnixListener() {if (m_bound) unlink(m_address.c_str());}
219
220     bool create(ShibSocket& s) const;
221     bool bind(ShibSocket& s, bool force=false) const;
222     bool connect(ShibSocket& s) const;
223     bool close(ShibSocket& s) const;
224     bool accept(ShibSocket& listener, ShibSocket& s) const;
225     CLIENT* getClientHandle(ShibSocket& s, u_long program, u_long version) const;
226     
227 private:
228     string m_address;
229     mutable bool m_bound;
230 };
231
232 IPlugIn* UnixListenerFactory(const DOMElement* e)
233 {
234     return new UnixListener(e);
235 }
236
237 UnixListener::UnixListener(const DOMElement* e) : RPCListener(e), m_address("/var/run/shar-socket"), m_bound(false)
238 {
239     // We're stateless, but we need to load the configuration.
240     const XMLCh* tag=e->getAttributeNS(NULL,address);
241     if (tag && *tag) {
242         auto_ptr_char a(tag);
243         m_address=a.get();
244     }
245 }
246
247 #ifndef UNIX_PATH_MAX
248 #define UNIX_PATH_MAX 100
249 #endif
250
251 bool UnixListener::create(ShibSocket& sock) const
252 {
253     sock = socket(PF_UNIX, SOCK_STREAM, 0);
254     if (sock < 0)
255         return log_error();
256     return true;
257 }
258
259 bool UnixListener::bind(ShibSocket& s, bool force) const
260 {
261     struct sockaddr_un addr;
262     memset(&addr, 0, sizeof (addr));
263     addr.sun_family = AF_UNIX;
264     strncpy(addr.sun_path, m_address.c_str(), UNIX_PATH_MAX);
265
266     if (force)
267         unlink(m_address.c_str());
268
269     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
270         log_error();
271         close(s);
272         return false;
273     }
274
275     // Make sure that only the creator can read -- we don't want just
276     // anyone connecting, do we?
277     if (chmod(m_address.c_str(),0777) < 0) {
278         log_error();
279         close(s);
280         unlink(m_address.c_str());
281         return false;
282     }
283
284     listen(s, 3);
285     return m_bound=true;
286 }
287
288 bool UnixListener::connect(ShibSocket& s) const
289 {
290     struct sockaddr_un addr;
291     memset(&addr, 0, sizeof (addr));
292     addr.sun_family = AF_UNIX;
293     strncpy(addr.sun_path, m_address.c_str(), UNIX_PATH_MAX);
294
295     if (::connect(s, (struct sockaddr *)&addr, sizeof (addr)) < 0)
296         return log_error();
297     return true;
298 }
299
300 bool UnixListener::close(ShibSocket& s) const
301 {
302     ::close(s);
303     return true;
304 }
305
306 bool UnixListener::accept(ShibSocket& listener, ShibSocket& s) const
307 {
308     s=::accept(listener,NULL,NULL);
309     if (s < 0)
310         return log_error();
311     return true;
312 }
313
314 CLIENT* UnixListener::getClientHandle(ShibSocket& s, u_long program, u_long version) const
315 {
316     struct sockaddr_in sin;
317     memset (&sin, 0, sizeof (sin));
318     sin.sin_port = 1;
319     return clnttcp_create(&sin, program, version, &s, 0, 0);
320 }
321
322 #endif /* !WIN32 */