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