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