monotone

monotone Mtn Source Tree

Root/netxx/datagram.cxx

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::Datagram class.
35**/
36
37// common header
38#include "common.h"
39
40// Netxx includes
41#include "netxx/datagram.h"
42#include "netxx/address.h"
43#include "netxx/peer.h"
44#include "socket.h"
45#include "sockaddr.h"
46#include "recvfrom.h"
47
48// standard includes
49#include <string>
50#include <memory>
51#include <cstring>
52#include <vector>
53
54//####################################################################
55namespace
56{
57 void call_connect(Netxx::Socket &socket, const Netxx::Peer &peer, std::vector<std::string> &files);
58 void call_bind(Netxx::Socket &socket, std::vector<std::string> &files);
59 void get_tmp_filename (std::string &tmp_name);
60}
61//####################################################################
62struct Netxx::Datagram::pimpl
63{
64 pimpl (const Timeout &timeout) : timeout_(timeout) {}
65 pimpl (const Timeout &timeout, socket_type socketfd) : timeout_(timeout), socket_(socketfd) {}
66
67 ~pimpl (void)
68 {
69# ifndef WIN32
70if (!files_.empty()) {
71 socket_.close();
72
73 std::vector<std::string>::const_iterator i(files_.begin()), end(files_.end());
74 for (; i!=end; ++i) unlink(i->c_str());
75}
76# endif
77 }
78
79 Timeout timeout_;
80 Socket socket_;
81 ProbeInfo pi_;
82 std::vector<std::string> files_;
83};
84//####################################################################
85Netxx::Datagram::Datagram (const Address &connect_to, const Timeout &timeout)
86{
87 if (!connect_to.size()) throw Exception("no addresses to call connect for");
88 std::auto_ptr<pimpl> ap(pimpl_ = new pimpl(timeout));
89
90 call_connect(pimpl_->socket_, *connect_to.begin(), pimpl_->files_);
91 pimpl_->pi_.add_socket(pimpl_->socket_.get_socketfd());
92
93 ap.release();
94}
95//####################################################################
96Netxx::Datagram::Datagram (const char *peer_name, port_type default_port, const Timeout &timeout)
97{
98 Address connect_to(peer_name, default_port);
99 std::auto_ptr<pimpl> ap(pimpl_ = new pimpl(timeout));
100
101 call_connect(pimpl_->socket_, *connect_to.begin(), pimpl_->files_);
102 pimpl_->pi_.add_socket(pimpl_->socket_.get_socketfd());
103
104 ap.release();
105}
106//####################################################################
107Netxx::Datagram::Datagram (socket_type socketfd, const Timeout &timeout)
108{
109 std::auto_ptr<pimpl> ap (pimpl_ = new pimpl(timeout, socketfd));
110
111 pimpl_->pi_.add_socket(pimpl_->socket_.get_socketfd());
112
113 ap.release();
114}
115//####################################################################
116Netxx::Datagram::Datagram (const Timeout &timeout)
117{
118 std::auto_ptr<pimpl> ap (pimpl_ = new pimpl(timeout));
119
120 pimpl_->pi_.add_socket(pimpl_->socket_.get_socketfd());
121
122 ap.release();
123}
124//####################################################################
125Netxx::Datagram::~Datagram (void)
126{
127 delete pimpl_;
128}
129//####################################################################
130Netxx::signed_size_type Netxx::Datagram::send (const Peer &peer, const void *buffer, size_type length)
131{
132# if defined(WIN32)
133const char *buffer_ptr = static_cast<const char*>(buffer);
134# else
135const void *buffer_ptr = buffer;
136# endif
137
138 const sockaddr *sa = reinterpret_cast<const sockaddr*>(peer.get_sa());
139 size_type sa_size = peer.get_sa_size();
140 bool correct_type = false;
141
142 /*
143 * make sure our socket is setup to handle this type of peer
144 */
145 if (peer.get_socketfd() != -1 && peer.get_socketfd() == pimpl_->socket_.get_socketfd()) {
146// we have to be ready!
147correct_type = true;
148 } else if (pimpl_->socket_.get_socketfd() >= 0) {
149Socket::Type stype = pimpl_->socket_.get_type();
150
151switch (stype) {
152# ifndef WIN32
153 case Socket::LOCALDGRAM:
154correct_type = sa->sa_family == AF_LOCAL;
155break;
156# endif
157
158 case Socket::UDP:
159correct_type = sa->sa_family == AF_INET;
160break;
161
162# ifndef NETXX_NO_INET6
163 case Socket::UDP6:
164correct_type = sa->sa_family == AF_INET6;
165break;
166# endif
167
168 default:
169break;
170}
171 }
172
173 if (!correct_type) {
174Socket::Type stype;
175
176switch (sa->sa_family) {
177 case AF_INET:
178stype = Socket::UDP;
179break;
180
181# ifndef NETXX_NO_INET6
182 case AF_INET6:
183stype = Socket::UDP6;
184break;
185# endif
186
187# ifndef WIN32
188 case AF_LOCAL:
189stype = Socket::LOCALDGRAM;
190break;
191# endif
192 default:
193stype = Socket::UDP;
194}
195
196Socket tmp_socket(stype);
197pimpl_->socket_.swap(tmp_socket);
198call_bind(pimpl_->socket_, pimpl_->files_);
199 }
200
201 if (pimpl_->timeout_ && !pimpl_->socket_.writable(pimpl_->timeout_))
202return -1;
203
204 signed_size_type rc;
205 if ( (rc = sendto(pimpl_->socket_.get_socketfd(), buffer_ptr, length, 0, sa, sa_size)) < 0) {
206std::string error("sendto(2) failure: ");
207error += str_error(get_last_error());
208throw Exception(error);
209 }
210
211 return rc;
212}
213//####################################################################
214Netxx::Datagram::receive_type Netxx::Datagram::receive (void *buffer, size_type length)
215{
216 /*
217 * if this is not a connected datagram, the socket might not be ready
218 * yet. We can't receive a datagram because we don't know if it should
219 * be a AF_INET, AF_INET6 or even a AF_LOCAL datagram.
220 */
221 if (pimpl_->socket_.get_socketfd() < 0)
222throw Exception("can't receive datagram unless one is sent first");
223
224 if (pimpl_->timeout_ && !pimpl_->socket_.readable(pimpl_->timeout_))
225return std::make_pair(-1, Peer());
226
227 return call_recvfrom(pimpl_->socket_, buffer, length);
228}
229//####################################################################
230Netxx::signed_size_type Netxx::Datagram::write (const void *buffer, size_type length)
231{
232 return pimpl_->socket_.write(buffer, length, pimpl_->timeout_);
233}
234//####################################################################
235Netxx::signed_size_type Netxx::Datagram::read (void *buffer, size_type length)
236{
237 return pimpl_->socket_.read(buffer, length, pimpl_->timeout_);
238}
239//####################################################################
240void Netxx::Datagram::close (void)
241{
242 pimpl_->socket_.close();
243 pimpl_->pi_.clear();
244}
245//####################################################################
246void Netxx::Datagram::release (void)
247{
248 pimpl_->socket_.release();
249}
250//####################################################################
251const Netxx::ProbeInfo* Netxx::Datagram::get_probe_info (void) const
252{
253 return &(pimpl_->pi_);
254}
255//####################################################################
256namespace
257{
258 //####################################################################
259 void call_connect (Netxx::Socket &socket, const Netxx::Peer &peer, std::vector<std::string> &files)
260 {
261const sockaddr *sa = reinterpret_cast<const sockaddr*>(peer.get_sa());
262Netxx::size_type sa_size = peer.get_sa_size();
263Netxx::Socket::Type stype;
264
265switch (sa->sa_family) {
266 case AF_INET:
267stype = Netxx::Socket::UDP;
268break;
269
270
271# ifndef NETXX_NO_INET6
272 case AF_INET6:
273stype = Netxx::Socket::UDP6;
274break;
275# endif
276
277# ifndef WIN32
278 case AF_LOCAL:
279stype = Netxx::Socket::LOCALDGRAM;
280break;
281# endif
282
283 default:
284stype = Netxx::Socket::UDP;
285}
286
287Netxx::Socket tmp_socket(stype);
288socket.swap(tmp_socket);
289call_bind(socket, files);
290
291if (connect(socket.get_socketfd(), sa, sa_size) != 0) {
292 std::string error("connect(2) failed: ");
293 error += Netxx::str_error(Netxx::get_last_error());
294 throw Netxx::NetworkException(error);
295}
296 }
297 //####################################################################
298 void call_bind (Netxx::Socket &socket, std::vector<std::string> &files)
299 {
300#ifndef WIN32
301 if (socket.get_type() == Netxx::Socket::LOCALDGRAM) {
302Netxx::SockAddr socket_addr(socket.get_type());
303sockaddr_un *saun = reinterpret_cast<sockaddr_un*>(socket_addr.get_sa());
304Netxx::size_type saun_size = socket_addr.get_sa_size();
305
306std::string tmp_name;
307get_tmp_filename(tmp_name);
308files.push_back(tmp_name);
309
310if (tmp_name.size() >= sizeof(saun->sun_path))
311 throw Netxx::Exception("temp name is too large for sockaddr_un");
312
313std::strcpy(saun->sun_path, tmp_name.c_str());
314
315if (bind(socket.get_socketfd(), reinterpret_cast<sockaddr*>(saun), saun_size) != 0) {
316 std::string error("bind(2) error: ");
317 error += Netxx::str_error(Netxx::get_last_error());
318 throw Netxx::NetworkException(error);
319}
320
321/*
322 * This will make a local domain socket more like a real
323 * socket since anyone with local file system access can
324 * connect to it. This may be a bad thing in this case
325 * though. More to come.
326 */
327chmod(saun->sun_path, 0666);
328 }
329#endif
330 }
331 //####################################################################
332 void get_tmp_filename (std::string &tmp_name)
333 {
334#ifndef WIN32
335 char buffer[] = "/tmp/Netxx_Domain_Socket.XXXXXXXXXXXXXXXXX";
336 int fd;
337
338 if ( (fd = mkstemp(buffer)) < 0) {
339std::string error("can't create temporary file: ");
340error += Netxx::str_error(Netxx::get_last_error());
341throw Netxx::NetworkException(error);
342 }
343
344 /*
345 * WARNING WARNING WARNING WARNING
346 *
347 * The following action could create a race condition. It may
348 * not be that big of a deal in this case since when we call
349 * bind(2) and it will fail if the file in sun_path already
350 * exists.
351 *
352 * So, if we close and unlink the file, and then before we call
353 * bind(2) someone creates a file with the same name, bind
354 * should fail.
355 *
356 * It might just be better to use tmpnam(3) but then you have to
357 * see the linker warning about tmpnam might be used in an
358 * unsafe manner. I think what we have here should be good
359 * enough.
360 *
361 * WARNING WARNING WARNING WARNING
362 */
363 close(fd);
364 unlink(buffer);
365
366 tmp_name = buffer;
367#endif
368 }
369 //####################################################################
370} // end anonymous namespace

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status