Replaced RPC remoting with plain sockets and length-prefixed XML.
[shibboleth/cpp-sp.git] / shib-target / TCPListener.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  * TCPListener.cpp
19  * 
20  * TCP-based SocketListener implementation
21  */
22
23 #include "SocketListener.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 SocketListener
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     
59     int send(ShibSocket& s, const char* buf, int len) const {
60         return ::send(s, buf, len, 0);
61     }
62     
63     int recv(ShibSocket& s, char* buf, int buflen) const {
64         return ::recv(s, buf, buflen, 0);
65     }
66     
67 private:
68     void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
69
70     string m_address;
71     unsigned short m_port;
72     vector<string> m_acl;
73 };
74
75 IPlugIn* TCPListenerFactory(const DOMElement* e)
76 {
77     return new TCPListener(e);
78 }
79
80 TCPListener::TCPListener(const DOMElement* e) : SocketListener(e), m_address("127.0.0.1"), m_port(12345)
81 {
82     // We're stateless, but we need to load the configuration.
83     const XMLCh* tag=e->getAttributeNS(NULL,address);
84     if (tag && *tag) {
85         auto_ptr_char a(tag);
86         m_address=a.get();
87     }
88     
89     tag=e->getAttributeNS(NULL,port);
90     if (tag && *tag) {
91         m_port=XMLString::parseInt(tag);
92         if (m_port==0)
93             m_port=12345;
94     }
95     
96     tag=e->getAttributeNS(NULL,SHIBT_L(acl));
97     if (tag && *tag) {
98         auto_ptr_char temp(tag);
99         string sockacl=temp.get();
100         if (sockacl.length()) {
101             int j = 0;
102             for (unsigned int i=0;  i < sockacl.length();  i++) {
103                 if (sockacl.at(i)==' ') {
104                     m_acl.push_back(sockacl.substr(j, i-j));
105                     j = i+1;
106                 }
107             }
108             m_acl.push_back(sockacl.substr(j, sockacl.length()-j));
109         }
110     }
111     else
112         m_acl.push_back("127.0.0.1");
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     for (vector<string>::const_iterator i=m_acl.begin(); i!=m_acl.end(); i++) {
202         if (*i==client)
203             return true;
204     }
205     close(s);
206     s=-1;
207     log->error("accept() rejected client at %s\n",client);
208     return false;
209 }