2daf68af4268fc6d1438cb066d287cf919e9bfd0
[shibboleth/cpp-sp.git] / shibsp / remoting / impl / TCPListener.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * TCPListener.cpp
23  *
24  * TCP-based SocketListener implementation.
25  */
26
27 #include "internal.h"
28 #include "remoting/impl/SocketListener.h"
29
30 #include <xercesc/util/XMLUniDefs.hpp>
31 #include <xmltooling/unicode.h>
32 #include <xmltooling/util/XMLHelper.h>
33
34 #ifdef HAVE_UNISTD_H
35 # include <sys/socket.h>
36 # include <sys/un.h>
37 # include <unistd.h>
38 # include <arpa/inet.h>
39 # include <netinet/in.h>
40 #endif
41
42 #include <sys/types.h>
43 #include <sys/stat.h>           /* for chmod() */
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <errno.h>
47
48 using namespace shibsp;
49 using namespace xmltooling;
50 using namespace xercesc;
51 using namespace std;
52
53 namespace shibsp {
54     class TCPListener : virtual public SocketListener
55     {
56     public:
57         TCPListener(const DOMElement* e);
58         ~TCPListener() {}
59
60         bool create(ShibSocket& s) const;
61         bool bind(ShibSocket& s, bool force=false) const;
62         bool connect(ShibSocket& s) const;
63         bool close(ShibSocket& s) const;
64         bool accept(ShibSocket& listener, ShibSocket& s) const;
65
66         int send(ShibSocket& s, const char* buf, int len) const {
67             return ::send(s, buf, len, 0);
68         }
69
70         int recv(ShibSocket& s, char* buf, int buflen) const {
71             return ::recv(s, buf, buflen, 0);
72         }
73
74     private:
75         void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
76
77         string m_address;
78         unsigned short m_port;
79         set<string> m_acl;
80     };
81
82     ListenerService* SHIBSP_DLLLOCAL TCPListenerServiceFactory(const DOMElement* const & e)
83     {
84         return new TCPListener(e);
85     }
86
87     static const XMLCh address[] = UNICODE_LITERAL_7(a,d,d,r,e,s,s);
88     static const XMLCh port[] = UNICODE_LITERAL_4(p,o,r,t);
89     static const XMLCh acl[] = UNICODE_LITERAL_3(a,c,l);
90 };
91
92 TCPListener::TCPListener(const DOMElement* e)
93     : SocketListener(e),
94       m_address(XMLHelper::getAttrString(e, getenv("SHIBSP_LISTENER_ADDRESS"), address)),
95       m_port(XMLHelper::getAttrInt(e, 0, port))
96 {
97     if (m_address.empty())
98         m_address = "127.0.0.1";
99
100     if (m_port == 0) {
101         const char* p = getenv("SHIBSP_LISTENER_PORT");
102         if (p && *p)
103             m_port = atoi(p);
104         if (m_port == 0)
105             m_port = 1600;
106     }
107
108     int j = 0;
109     string sockacl = XMLHelper::getAttrString(e, "127.0.0.1", acl);
110     for (unsigned int i = 0;  i < sockacl.length();  i++) {
111         if (sockacl.at(i) == ' ') {
112             m_acl.insert(sockacl.substr(j, i-j));
113             j = i+1;
114         }
115     }
116     m_acl.insert(sockacl.substr(j, sockacl.length()-j));
117 }
118
119 void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const
120 {
121     // Split on host:port boundary. Default to port only.
122     memset(addr,0,sizeof(struct sockaddr_in));
123     addr->sin_family=AF_INET;
124     addr->sin_port=htons(m_port);
125     addr->sin_addr.s_addr=inet_addr(m_address.c_str());
126 }
127
128 bool TCPListener::create(ShibSocket& s) const
129 {
130     s=socket(AF_INET,SOCK_STREAM,0);
131 #ifdef WIN32
132     if(s==INVALID_SOCKET)
133 #else
134     if (s < 0)
135 #endif
136         return log_error();
137     return true;
138 }
139
140 bool TCPListener::bind(ShibSocket& s, bool force) const
141 {
142     struct sockaddr_in addr;
143     setup_tcp_sockaddr(&addr);
144
145     // XXX: Do we care about the return value from setsockopt?
146     int opt = 1;
147     ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
148
149 #ifdef WIN32
150     if (SOCKET_ERROR==::bind(s,(struct sockaddr *)&addr,sizeof(addr)) || SOCKET_ERROR==::listen(s,3)) {
151         log_error();
152         close(s);
153         return false;
154     }
155 #else
156     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
157         log_error();
158         close(s);
159         return false;
160     }
161     ::listen(s,3);
162 #endif
163     return true;
164 }
165
166 bool TCPListener::connect(ShibSocket& s) const
167 {
168     struct sockaddr_in addr;
169     setup_tcp_sockaddr(&addr);
170 #ifdef WIN32
171     if(SOCKET_ERROR==::connect(s,(struct sockaddr *)&addr,sizeof(addr)))
172         return log_error();
173 #else
174     if (::connect(s, (struct sockaddr*)&addr, sizeof (addr)) < 0)
175         return log_error();
176 #endif
177     return true;
178 }
179
180 bool TCPListener::close(ShibSocket& s) const
181 {
182 #ifdef WIN32
183     closesocket(s);
184 #else
185     ::close(s);
186 #endif
187     return true;
188 }
189
190 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
191 {
192     struct sockaddr_in addr;
193
194 #ifdef WIN32
195     int size=sizeof(addr);
196     s=::accept(listener,(struct sockaddr*)&addr,&size);
197     if(s==INVALID_SOCKET)
198 #else
199     socklen_t size=sizeof(addr);
200     s=::accept(listener,(struct sockaddr*)&addr,&size);
201     if (s < 0)
202 #endif
203         return log_error();
204     char* client=inet_ntoa(addr.sin_addr);
205     if (m_acl.count(client) == 0) {
206         close(s);
207         s=-1;
208         log->error("accept() rejected client at %s", client);
209         return false;
210     }
211     return true;
212 }