05f2f9070f869879efbf87dcc4add450114c78a5
[shibboleth/sp.git] / shibsp / remoting / impl / TCPListener.cpp
1 /*
2  *  Copyright 2001-2009 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  * TCPListener.cpp
19  *
20  * TCP-based SocketListener implementation
21  */
22
23 #include "internal.h"
24 #include "remoting/impl/SocketListener.h"
25
26 #include <xercesc/util/XMLUniDefs.hpp>
27 #include <xmltooling/unicode.h>
28
29 #ifdef HAVE_UNISTD_H
30 # include <sys/socket.h>
31 # include <sys/un.h>
32 # include <unistd.h>
33 # include <arpa/inet.h>
34 # include <netinet/in.h>
35 #endif
36
37 #include <sys/types.h>
38 #include <sys/stat.h>           /* for chmod() */
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <errno.h>
42
43 using namespace shibsp;
44 using namespace xmltooling;
45 using namespace xercesc;
46 using namespace std;
47
48 namespace shibsp {
49     static const XMLCh address[] = UNICODE_LITERAL_7(a,d,d,r,e,s,s);
50     static const XMLCh port[] = UNICODE_LITERAL_4(p,o,r,t);
51     static const XMLCh acl[] = UNICODE_LITERAL_3(a,c,l);
52
53     class TCPListener : virtual public SocketListener
54     {
55     public:
56         TCPListener(const DOMElement* e);
57         ~TCPListener() {}
58
59         bool create(ShibSocket& s) const;
60         bool bind(ShibSocket& s, bool force=false) const;
61         bool connect(ShibSocket& s) const;
62         bool close(ShibSocket& s) const;
63         bool accept(ShibSocket& listener, ShibSocket& s) const;
64
65         int send(ShibSocket& s, const char* buf, int len) const {
66             return ::send(s, buf, len, 0);
67         }
68
69         int recv(ShibSocket& s, char* buf, int buflen) const {
70             return ::recv(s, buf, buflen, 0);
71         }
72
73     private:
74         void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
75
76         string m_address;
77         unsigned short m_port;
78         set<string> m_acl;
79     };
80
81     ListenerService* SHIBSP_DLLLOCAL TCPListenerServiceFactory(const DOMElement* const & e)
82     {
83         return new TCPListener(e);
84     }
85 };
86
87 TCPListener::TCPListener(const DOMElement* e) : SocketListener(e), m_address("127.0.0.1"), m_port(12345)
88 {
89     // We're stateless, but we need to load the configuration.
90     const XMLCh* tag=e->getAttributeNS(NULL,address);
91     if (tag && *tag) {
92         auto_ptr_char a(tag);
93         m_address=a.get();
94     }
95
96     tag=e->getAttributeNS(NULL,port);
97     if (tag && *tag) {
98         m_port=XMLString::parseInt(tag);
99         if (m_port==0)
100             m_port=12345;
101     }
102
103     tag=e->getAttributeNS(NULL,acl);
104     if (tag && *tag) {
105         auto_ptr_char temp(tag);
106         string sockacl=temp.get();
107         if (sockacl.length()) {
108             int j = 0;
109             for (unsigned int i=0;  i < sockacl.length();  i++) {
110                 if (sockacl.at(i)==' ') {
111                     m_acl.insert(sockacl.substr(j, i-j));
112                     j = i+1;
113                 }
114             }
115             m_acl.insert(sockacl.substr(j, sockacl.length()-j));
116         }
117     }
118     else
119         m_acl.insert("127.0.0.1");
120 }
121
122 void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const
123 {
124     // Split on host:port boundary. Default to port only.
125     memset(addr,0,sizeof(struct sockaddr_in));
126     addr->sin_family=AF_INET;
127     addr->sin_port=htons(m_port);
128     addr->sin_addr.s_addr=inet_addr(m_address.c_str());
129 }
130
131 bool TCPListener::create(ShibSocket& s) const
132 {
133     s=socket(AF_INET,SOCK_STREAM,0);
134 #ifdef WIN32
135     if(s==INVALID_SOCKET)
136 #else
137     if (s < 0)
138 #endif
139         return log_error();
140     return true;
141 }
142
143 bool TCPListener::bind(ShibSocket& s, bool force) const
144 {
145     struct sockaddr_in addr;
146     setup_tcp_sockaddr(&addr);
147
148     // XXX: Do we care about the return value from setsockopt?
149     int opt = 1;
150     ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
151
152 #ifdef WIN32
153     if (SOCKET_ERROR==::bind(s,(struct sockaddr *)&addr,sizeof(addr)) || SOCKET_ERROR==::listen(s,3)) {
154         log_error();
155         close(s);
156         return false;
157     }
158 #else
159     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
160         log_error();
161         close(s);
162         return false;
163     }
164     ::listen(s,3);
165 #endif
166     return true;
167 }
168
169 bool TCPListener::connect(ShibSocket& s) const
170 {
171     struct sockaddr_in addr;
172     setup_tcp_sockaddr(&addr);
173 #ifdef WIN32
174     if(SOCKET_ERROR==::connect(s,(struct sockaddr *)&addr,sizeof(addr)))
175         return log_error();
176 #else
177     if (::connect(s, (struct sockaddr*)&addr, sizeof (addr)) < 0)
178         return log_error();
179 #endif
180     return true;
181 }
182
183 bool TCPListener::close(ShibSocket& s) const
184 {
185 #ifdef WIN32
186     closesocket(s);
187 #else
188     ::close(s);
189 #endif
190     return true;
191 }
192
193 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
194 {
195     struct sockaddr_in addr;
196
197 #ifdef WIN32
198     int size=sizeof(addr);
199     s=::accept(listener,(struct sockaddr*)&addr,&size);
200     if(s==INVALID_SOCKET)
201 #else
202     socklen_t size=sizeof(addr);
203     s=::accept(listener,(struct sockaddr*)&addr,&size);
204     if (s < 0)
205 #endif
206         return log_error();
207     char* client=inet_ntoa(addr.sin_addr);
208     if (m_acl.count(client) == 0) {
209         close(s);
210         s=-1;
211         log->error("accept() rejected client at %s", client);
212         return false;
213     }
214     return true;
215 }