1 /*
2  * Copyright (C) 2001-2004 Peter J Jones (pjones@pmade.org)
3  * All Rights Reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  * 3. Neither the name of the Author nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR
23  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /** @file
34  * This file contains the implementation of the Netxx::ServerBase class.
35 **/
36 
37 // common header
38 #include "common.h"
39 
40 // Netxx includes
41 #include "serverbase.h"
42 #include "address.h"
43 #include "sockopt.h"
44 #include "probeinfo.h"
45 #include "socket.h"
46 
47 #include <cerrno>
48 
49 // Monotone specific, for get_current_working_dir()
50 #include <src/platform.hh>
51 
52 // standard includes
53 #include <map>
54 #include <vector>
55 #include <algorithm>
56 #include <functional>
57 
58 //####################################################################
59 namespace
60 {
61 #   ifndef WIN32
62 	struct unlink_functor : public std::unary_function<std::string, void>
63 	{
operator ()__anon81f1bef20111::unlink_functor64 	    void operator() (const std::string &file)
65 	    { unlink(file.c_str()); }
66 	};
67 #   endif
68 }
69 //####################################################################
ServerBase(const Timeout & timeout)70 Netxx::ServerBase::ServerBase (const Timeout &timeout)
71     : timeout_(timeout), sockets_(0), sockets_size_(0)
72 { /* must not call bind_to from here */ }
73 //####################################################################
~ServerBase(void)74 Netxx::ServerBase::~ServerBase (void)
75 {
76 #   ifndef WIN32
77 	if (sockets_ && !files_.empty()) {
78 	    std::for_each(sockets_, sockets_ + sockets_size_, std::mem_fun_ref(&Socket::close));
79 	    std::for_each(files_.begin(), files_.end(), unlink_functor());
80 	}
81 #   endif
82 
83     if (sockets_) delete [] sockets_;
84 }
85 //####################################################################
bind_to(const Address & addr,bool stream_server)86 void Netxx::ServerBase::bind_to(const Address &addr, bool stream_server)
87 {
88     if (sockets_) throw Exception("bug in Netxx, ServerBase::sockets_ != 0");
89 
90     /*
91      * it is safe to allocate this block of memory because the destructor
92      * will free it even if this function dies with an exception.
93      */
94     sockets_size_ = addr.size();
95     sockets_ = new Socket[sockets_size_];
96 
97     /*
98      * now that we have an array of sockets we need to walk through each one
99      * and set it up. We will use a temporay socket and make it call
100      * socket() for us. Then we can set the reuse address socket option and
101      * call bind.
102      */
103     Address::const_iterator addr_it=addr.begin(), addr_end=addr.end();
104     for (size_type current_socket=0; addr_it != addr_end; ++addr_it, ++current_socket) {
105 	const sockaddr *sa = static_cast<const sockaddr*>(addr_it->get_sa());
106 	size_type sa_size = addr_it->get_sa_size();
107 
108 	Socket::Type stype;
109 	switch (sa->sa_family) {
110 	    case AF_INET:
111 		stype = stream_server ? Socket::TCP : Socket::UDP;
112 		break;
113 
114 #	ifndef NETXX_NO_INET6
115 	    case AF_INET6:
116 		stype = stream_server ? Socket::TCP6 : Socket::UDP6;
117 		break;
118 #	endif
119 
120 #	ifndef WIN32
121 	    case AF_LOCAL:
122 		stype = stream_server ? Socket::LOCALSTREAM : Socket::LOCALDGRAM;
123 		break;
124 #	endif
125 
126 	    default:
127 		stype = stream_server ? Socket::TCP : Socket::UDP;
128 		break;
129 	}
130 
131 	Socket tmp_socket(stype);
132 	socket_type socketfd = tmp_socket.get_socketfd();
133 	tmp_socket.swap(sockets_[current_socket]);
134 
135 	SockOpt socket_opt(socketfd);
136 	socket_opt.set_reuse_address();
137 
138 #	ifndef NETXX_NO_INET6
139 	if (sa->sa_family == AF_INET6)
140 	    socket_opt.set_ipv6_listen_for_v6_only();
141 #	endif
142 
143 	if (bind(socketfd, sa, sa_size) != 0) {
144 	    std::string error("bind(2) error: ");
145 	    error += Netxx::str_error(Netxx::get_last_error());
146 	    throw NetworkException(error);
147 	}
148 
149 	sockets_map_[socketfd] = &(sockets_[current_socket]);
150 	pi_.add_socket(socketfd);
151 
152 #	ifndef WIN32
153 	    /*
154 	     * check to see if we need to record a filename. we would need
155 	     * to record a filename for local domain sockets in order to
156 	     * remove the file when the server is done with it.
157 	     *
158 	     * also take this time to set the mode for the socket file to
159 	     * something sane. this may be a bad assumption here but I think
160 	     * this is a good value for a domain socket.
161 	     */
162 	    if (sa->sa_family == AF_LOCAL) {
163 		const sockaddr_un *saun = reinterpret_cast<const sockaddr_un*>(sa);
164 
165 		/*
166 		 * This will make a local domain socket more like a real
167 		 * socket since anyone with local file system access can
168 		 * connect to it.
169 		 */
170 		chmod(saun->sun_path, 0666);
171 
172 		if (saun->sun_path[0] == '/') {
173 		    files_.push_back(saun->sun_path);
174 		} else {
175 		    /*
176 		     * the original (netxx) code here relied on MAXPATHLEN,
177 		     * which isn't defined on hurd. As netxx seems to only
178 		     * live on within monotone, we can as well make it
179 		     * inter-dependent. And the unix/fs.cc variant certainly
180 		     * gets more test mileage than anything special here.
181 		     */
182 		    std::string fullpath = get_current_working_dir();
183 		    fullpath += '/'; fullpath += saun->sun_path;
184 		    files_.push_back(fullpath);
185 		}
186 	    }
187 #	endif
188     }
189 
190     probe_.add(*this);
191 }
192 //####################################################################
get_socket_list(Socket * & sockets,size_type & size)193 void Netxx::ServerBase::get_socket_list (Socket *&sockets, size_type &size)
194 {
195     sockets = sockets_;
196     size = sockets_size_;
197 }
198 //####################################################################
get_readable_socket(void)199 Netxx::Socket* Netxx::ServerBase::get_readable_socket (void)
200 {
201     Probe::result_type rt = probe_.ready(timeout_, Probe::ready_read);
202     if (rt.first == -1) return 0;
203     return sockets_map_[rt.first];
204 }
205 //####################################################################
set_timeout(const Timeout & timeout)206 void Netxx::ServerBase::set_timeout (const Timeout &timeout)
207 {
208     timeout_ = timeout;
209 }
210 //####################################################################
get_timeout(void) const211 const Netxx::Timeout& Netxx::ServerBase::get_timeout (void) const
212 {
213     return timeout_;
214 }
215 //####################################################################
get_probe_info(void) const216 const Netxx::ProbeInfo* Netxx::ServerBase::get_probe_info (void) const
217 {
218     return &pi_;
219 }
220 //####################################################################
has_socket(socket_type socketfd) const221 bool Netxx::ServerBase::has_socket (socket_type socketfd) const
222 {
223     return sockets_map_.find(socketfd) != sockets_map_.end();
224 }
225 //####################################################################
226