Refactor TCP dumping
This is in preparation for having JMD dumper also support dump over TCP/IP. Also fix some bugs in dumping (especially over TCP/IP)
This commit is contained in:
parent
33187dedde
commit
b00ccb2731
5 changed files with 286 additions and 218 deletions
|
@ -69,6 +69,8 @@ struct avi_video_codec
|
||||||
* Parameter fps_n: fps numerator.
|
* Parameter fps_n: fps numerator.
|
||||||
* Parameter fps_d: fps denominator.
|
* Parameter fps_d: fps denominator.
|
||||||
* Returns: Stream format.
|
* Returns: Stream format.
|
||||||
|
*
|
||||||
|
* Note: The next frame emitted MUST be a keyframe.
|
||||||
*/
|
*/
|
||||||
virtual format reset(uint32_t width, uint32_t height, uint32_t fps_n, uint32_t fps_d) = 0;
|
virtual format reset(uint32_t width, uint32_t height, uint32_t fps_n, uint32_t fps_d) = 0;
|
||||||
/**
|
/**
|
||||||
|
|
23
include/video/tcp.hpp
Normal file
23
include/video/tcp.hpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef _tcp__hpp__included__
|
||||||
|
#define _tcp__hpp__included__
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class socket_address
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
socket_address(const std::string& spec);
|
||||||
|
socket_address next();
|
||||||
|
std::ostream& connect();
|
||||||
|
static bool supported();
|
||||||
|
private:
|
||||||
|
socket_address(int f, int st, int p);
|
||||||
|
int family;
|
||||||
|
int socktype;
|
||||||
|
int protocol;
|
||||||
|
std::vector<char> memory;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -96,10 +96,19 @@ void dumper_menu::on_select(wxCommandEvent& e)
|
||||||
if(id < wxid_range_low || id > wxid_range_high)
|
if(id < wxid_range_low || id > wxid_range_high)
|
||||||
return;
|
return;
|
||||||
for(auto i : menustructure) {
|
for(auto i : menustructure) {
|
||||||
|
std::string error_str;
|
||||||
adv_dumper* t = existing_dumpers[i.first].instance;
|
adv_dumper* t = existing_dumpers[i.first].instance;
|
||||||
if(i.second.end_wxid == id) {
|
if(i.second.end_wxid == id) {
|
||||||
//Execute end of dump operation.
|
//Execute end of dump operation.
|
||||||
runemufn([t]() { t->end(); });
|
runemufn([t, &error_str]() {
|
||||||
|
try {
|
||||||
|
t->end();
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
error_str = e.what();
|
||||||
|
}});
|
||||||
|
if(error_str != "")
|
||||||
|
wxMessageBox(towxstring(error_str), _T("Error ending dump"), wxICON_EXCLAMATION | wxOK,
|
||||||
|
pwin);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(i.second.start_wxids.count(id)) {
|
if(i.second.start_wxids.count(id)) {
|
||||||
|
@ -110,11 +119,19 @@ void dumper_menu::on_select(wxCommandEvent& e)
|
||||||
wxFileDialog* d = new wxFileDialog(pwin, towxstring(prefixed ? std::string("Choose prefix") :
|
wxFileDialog* d = new wxFileDialog(pwin, towxstring(prefixed ? std::string("Choose prefix") :
|
||||||
std::string("Choose file")), wxT("."));
|
std::string("Choose file")), wxT("."));
|
||||||
if(d->ShowModal() == wxID_OK)
|
if(d->ShowModal() == wxID_OK)
|
||||||
prefix = tostdstring(d->GetPath());
|
prefix = tostdstring(d->GetFilename());
|
||||||
d->Destroy();
|
d->Destroy();
|
||||||
if(prefix == "")
|
if(prefix == "")
|
||||||
return;
|
return;
|
||||||
runemufn([t, mode, prefix]() { t->start(mode, prefix); });
|
runemufn([t, mode, prefix, &error_str]() {
|
||||||
|
try {
|
||||||
|
t->start(mode, prefix);
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
error_str = e.what();
|
||||||
|
}});
|
||||||
|
if(error_str != "")
|
||||||
|
wxMessageBox(towxstring(error_str), _T("Error starting dump"), wxICON_EXCLAMATION |
|
||||||
|
wxOK, pwin);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "core/advdumper.hpp"
|
#include "core/advdumper.hpp"
|
||||||
#include "core/dispatch.hpp"
|
#include "core/dispatch.hpp"
|
||||||
|
#include "video/tcp.hpp"
|
||||||
#include "library/serialization.hpp"
|
#include "library/serialization.hpp"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
@ -10,29 +11,6 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#ifndef NO_TCP_SOCKETS
|
|
||||||
#include <boost/iostreams/categories.hpp>
|
|
||||||
#include <boost/iostreams/copy.hpp>
|
|
||||||
#include <boost/iostreams/stream.hpp>
|
|
||||||
#include <boost/iostreams/stream_buffer.hpp>
|
|
||||||
#include <boost/iostreams/filter/symmetric.hpp>
|
|
||||||
#include <boost/iostreams/filter/zlib.hpp>
|
|
||||||
#include <boost/iostreams/filtering_stream.hpp>
|
|
||||||
#include <boost/iostreams/device/back_inserter.hpp>
|
|
||||||
#include <unistd.h>
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
//Why the fuck does windows have nonstandard socket API???
|
|
||||||
#define _WIN32_WINNT 0x0501
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
struct sockaddr_un { int sun_family; char sun_path[108]; };
|
|
||||||
#else
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define IS_RGB(m) (((m) + ((m) >> 3)) & 2)
|
#define IS_RGB(m) (((m) + ((m) >> 3)) & 2)
|
||||||
#define IS_64(m) (m % 5 < 2)
|
#define IS_64(m) (m % 5 < 2)
|
||||||
|
@ -42,194 +20,6 @@ struct sockaddr_un { int sun_family; char sun_path[108]; };
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
#ifndef NO_TCP_SOCKETS
|
|
||||||
//Increment port number by 1.
|
|
||||||
void mung_sockaddr(struct sockaddr* addr, socklen_t addrlen)
|
|
||||||
{
|
|
||||||
switch(addr->sa_family) {
|
|
||||||
case AF_INET: { //IPv4
|
|
||||||
struct sockaddr_in* _addr = (struct sockaddr_in*)addr;
|
|
||||||
_addr->sin_port = htons(htons(_addr->sin_port) + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AF_INET6: { //IPv6
|
|
||||||
struct sockaddr_in6* _addr = (struct sockaddr_in6*)addr;
|
|
||||||
_addr->sin6_port = htons(htons(_addr->sin6_port) + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AF_UNIX: { //Unix domain sockets.
|
|
||||||
struct sockaddr_un* _addr = (struct sockaddr_un*)addr;
|
|
||||||
const char* b1 = (char*)_addr;
|
|
||||||
const char* b2 = (char*)&_addr->sun_path;
|
|
||||||
size_t maxpath = addrlen - (b2 - b1);
|
|
||||||
for(size_t i = 0; i < maxpath; i++)
|
|
||||||
if(i && !_addr->sun_path[i]) {
|
|
||||||
maxpath = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!maxpath)
|
|
||||||
throw std::runtime_error("Eh, empty unix domain socket path?");
|
|
||||||
_addr->sun_path[maxpath - 1]++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("This address family is not supported, sorry.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int compat_connect(int fd, struct sockaddr* addr, socklen_t addrlen)
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
return connect(fd, addr, addrlen) ? -1 : 0;
|
|
||||||
#else
|
|
||||||
return connect(fd, addr, addrlen);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<int, int> establish_connections(struct addrinfo* i)
|
|
||||||
{
|
|
||||||
struct sockaddr* addr = i->ai_addr;
|
|
||||||
socklen_t addrlen = i->ai_addrlen;
|
|
||||||
int a = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
|
|
||||||
if(a < 0) {
|
|
||||||
int err = errno;
|
|
||||||
throw std::runtime_error(std::string("socket: ") + strerror(err));
|
|
||||||
}
|
|
||||||
int b = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
|
|
||||||
if(b < 0) {
|
|
||||||
int err = errno;
|
|
||||||
close(a);
|
|
||||||
throw std::runtime_error(std::string("socket: ") + strerror(err));
|
|
||||||
}
|
|
||||||
if(compat_connect(a, addr, addrlen) < 0) {
|
|
||||||
int err = errno;
|
|
||||||
close(a);
|
|
||||||
close(b);
|
|
||||||
throw std::runtime_error(std::string("connect (video): ") + strerror(err));
|
|
||||||
}
|
|
||||||
mung_sockaddr(addr, addrlen);
|
|
||||||
if(compat_connect(b, addr, addrlen) < 0) {
|
|
||||||
int err = errno;
|
|
||||||
close(a);
|
|
||||||
close(b);
|
|
||||||
throw std::runtime_error(std::string("connect (audio): ") + strerror(err));
|
|
||||||
}
|
|
||||||
std::cerr << "Routing video to socket " << a << std::endl;
|
|
||||||
std::cerr << "Routing audio to socket " << b << std::endl;
|
|
||||||
|
|
||||||
return std::make_pair(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<int, int> get_sockets(const std::string& name)
|
|
||||||
{
|
|
||||||
struct addrinfo hints;
|
|
||||||
struct addrinfo* ainfo;
|
|
||||||
bool real = false;
|
|
||||||
int r;
|
|
||||||
std::string node, service, tmp = name;
|
|
||||||
size_t s;
|
|
||||||
struct sockaddr_un uaddr;
|
|
||||||
if(name[0] == '/' || name[0] == '@') {
|
|
||||||
//Fake a unix-domain.
|
|
||||||
if(name.length() >= sizeof(sockaddr_un) - offsetof(sockaddr_un, sun_path) - 1)
|
|
||||||
throw std::runtime_error("Path too long for filesystem socket");
|
|
||||||
size_t namelen = offsetof(struct sockaddr_un, sun_path) + name.length();
|
|
||||||
uaddr.sun_family = AF_UNIX;
|
|
||||||
strcpy(uaddr.sun_path, name.c_str());
|
|
||||||
if(name[0] == '@')
|
|
||||||
uaddr.sun_path[0] = 0; //Mark as abstract namespace socket.
|
|
||||||
ainfo = &hints;
|
|
||||||
ainfo->ai_flags = 0;
|
|
||||||
ainfo->ai_family = AF_UNIX;
|
|
||||||
ainfo->ai_socktype = SOCK_STREAM;
|
|
||||||
ainfo->ai_protocol = 0;
|
|
||||||
ainfo->ai_addrlen = (name[0] == '@') ? namelen : sizeof(sockaddr_un),
|
|
||||||
ainfo->ai_addr = reinterpret_cast<sockaddr*>(&uaddr);
|
|
||||||
ainfo->ai_canonname = NULL;
|
|
||||||
ainfo->ai_next = NULL;
|
|
||||||
goto establish;
|
|
||||||
}
|
|
||||||
//Split into address and port.
|
|
||||||
s = tmp.find_last_of(":");
|
|
||||||
if(s >= tmp.length())
|
|
||||||
throw std::runtime_error("Port number has to be specified");
|
|
||||||
node = tmp.substr(0, s);
|
|
||||||
service = tmp.substr(s + 1);
|
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_family = AF_UNSPEC;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
#ifdef AI_V4MAPPED
|
|
||||||
hints.ai_flags = AI_V4MAPPED;
|
|
||||||
#endif
|
|
||||||
#ifdef AI_ADDRCONFIG
|
|
||||||
hints.ai_flags = AI_ADDRCONFIG;
|
|
||||||
#endif
|
|
||||||
real = true;
|
|
||||||
r = getaddrinfo(node.c_str(), service.c_str(), &hints, &ainfo);
|
|
||||||
if(r < 0)
|
|
||||||
throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(r));
|
|
||||||
establish:
|
|
||||||
auto x = establish_connections(ainfo);
|
|
||||||
if(real)
|
|
||||||
freeaddrinfo(ainfo);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
class socket_output
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef char char_type;
|
|
||||||
typedef boost::iostreams::sink_tag category;
|
|
||||||
socket_output(int _fd)
|
|
||||||
: fd(_fd)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
::close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize write(const char* s, std::streamsize n)
|
|
||||||
{
|
|
||||||
size_t w = n;
|
|
||||||
while(n > 0) {
|
|
||||||
ssize_t r = ::send(fd, s, n, 0);
|
|
||||||
if(r >= 0) {
|
|
||||||
s += r;
|
|
||||||
n -= r;
|
|
||||||
} else { //Error.
|
|
||||||
int err = errno;
|
|
||||||
messages << "Socket write error: " << strerror(err) << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
bool tcp_dump_supported = true;
|
|
||||||
#else
|
|
||||||
std::pair<int, int> get_sockets(const std::string& name)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Dumping over TCP/IP not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
class socket_output
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef char char_type;
|
|
||||||
typedef boost::iostreams::sink_tag category;
|
|
||||||
socket_output(int _fd) {}
|
|
||||||
void close() {}
|
|
||||||
std::streamsize write(const char* s, std::streamsize n) { return n; }
|
|
||||||
};
|
|
||||||
bool tcp_dump_supported = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
unsigned strhash(const std::string& str)
|
unsigned strhash(const std::string& str)
|
||||||
{
|
{
|
||||||
unsigned h = 0;
|
unsigned h = 0;
|
||||||
|
@ -246,9 +36,17 @@ establish:
|
||||||
{
|
{
|
||||||
enable_send_sound();
|
enable_send_sound();
|
||||||
if(socket_mode) {
|
if(socket_mode) {
|
||||||
std::pair<int, int> socks = get_sockets(prefix);
|
socket_address videoaddr = socket_address(prefix);
|
||||||
video = new boost::iostreams::stream<socket_output>(socks.first);
|
socket_address audioaddr = videoaddr.next();
|
||||||
audio = new boost::iostreams::stream<socket_output>(socks.second);
|
video = audio = NULL;
|
||||||
|
try {
|
||||||
|
video = &videoaddr.connect();
|
||||||
|
audio = &audioaddr.connect();
|
||||||
|
} catch(...) {
|
||||||
|
delete video;
|
||||||
|
delete audio;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
video = new std::ofstream(prefix + ".video", std::ios::out | std::ios::binary);
|
video = new std::ofstream(prefix + ".video", std::ios::out | std::ios::binary);
|
||||||
audio = new std::ofstream(prefix + ".audio", std::ios::out | std::ios::binary);
|
audio = new std::ofstream(prefix + ".audio", std::ios::out | std::ios::binary);
|
||||||
|
@ -336,7 +134,7 @@ establish:
|
||||||
std::set<std::string> list_submodes() throw(std::bad_alloc)
|
std::set<std::string> list_submodes() throw(std::bad_alloc)
|
||||||
{
|
{
|
||||||
std::set<std::string> x;
|
std::set<std::string> x;
|
||||||
for(size_t i = 0; i < (tcp_dump_supported ? 2 : 1); i++)
|
for(size_t i = 0; i < (socket_address::supported() ? 2 : 1); i++)
|
||||||
for(size_t j = 0; j < 2; j++)
|
for(size_t j = 0; j < 2; j++)
|
||||||
for(size_t k = 0; k < 2; k++)
|
for(size_t k = 0; k < 2; k++)
|
||||||
x.insert(std::string("") + (i ? "tcp" : "") + (j ? "bgr" : "rgb")
|
x.insert(std::string("") + (i ? "tcp" : "") + (j ? "bgr" : "rgb")
|
||||||
|
|
228
src/video/tcp.cpp
Normal file
228
src/video/tcp.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
#include "video/tcp.hpp"
|
||||||
|
|
||||||
|
#ifdef NO_TCP_SOCKETS
|
||||||
|
|
||||||
|
socket_address::socket_address(const std::string& spec)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("TCP/IP support not compiled in");
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_address socket_address::next()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& socket_address::connect()
|
||||||
|
{
|
||||||
|
throw std::runtime_error("TCP/IP support not compiled in");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool socket_address::supported()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_address::socket_address(int f, int st, int p)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("TCP/IP support not compiled in");
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <boost/iostreams/categories.hpp>
|
||||||
|
#include <boost/iostreams/copy.hpp>
|
||||||
|
#include <boost/iostreams/stream.hpp>
|
||||||
|
#include <boost/iostreams/stream_buffer.hpp>
|
||||||
|
#include <boost/iostreams/filter/symmetric.hpp>
|
||||||
|
#include <boost/iostreams/filter/zlib.hpp>
|
||||||
|
#include <boost/iostreams/filtering_stream.hpp>
|
||||||
|
#include <boost/iostreams/device/back_inserter.hpp>
|
||||||
|
#include <unistd.h>
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
//Why the fuck does windows have nonstandard socket API???
|
||||||
|
#define _WIN32_WINNT 0x0501
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
struct sockaddr_un { int sun_family; char sun_path[108]; };
|
||||||
|
#else
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class socket_output
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef char char_type;
|
||||||
|
typedef struct : public boost::iostreams::sink_tag, boost::iostreams::closable_tag {} category;
|
||||||
|
socket_output(int _fd)
|
||||||
|
: fd(_fd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize write(const char* s, std::streamsize n)
|
||||||
|
{
|
||||||
|
size_t w = n;
|
||||||
|
while(n > 0) {
|
||||||
|
ssize_t r = ::send(fd, s, n, 0);
|
||||||
|
if(r >= 0) {
|
||||||
|
s += r;
|
||||||
|
n -= r;
|
||||||
|
} else { //Error.
|
||||||
|
int err = errno;
|
||||||
|
std::cerr << "Socket write error: " << strerror(err) << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_address::socket_address(const std::string& name)
|
||||||
|
{
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo* ainfo;
|
||||||
|
bool real = false;
|
||||||
|
int r;
|
||||||
|
std::string node, service, tmp = name;
|
||||||
|
size_t s;
|
||||||
|
struct sockaddr_un uaddr;
|
||||||
|
if(name[0] == '/' || name[0] == '@') {
|
||||||
|
//Fake a unix-domain.
|
||||||
|
if(name.length() >= sizeof(sockaddr_un) - offsetof(sockaddr_un, sun_path) - 1)
|
||||||
|
throw std::runtime_error("Path too long for filesystem socket");
|
||||||
|
size_t namelen = offsetof(struct sockaddr_un, sun_path) + name.length();
|
||||||
|
uaddr.sun_family = AF_UNIX;
|
||||||
|
strcpy(uaddr.sun_path, name.c_str());
|
||||||
|
if(name[0] == '@')
|
||||||
|
uaddr.sun_path[0] = 0; //Mark as abstract namespace socket.
|
||||||
|
family = AF_UNIX;
|
||||||
|
socktype = SOCK_STREAM;
|
||||||
|
protocol = 0;
|
||||||
|
memory.resize((name[0] == '@') ? namelen : sizeof(sockaddr_un));
|
||||||
|
memcpy(&memory[0], &uaddr, memory.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Split into address and port.
|
||||||
|
s = tmp.find_last_of(":");
|
||||||
|
if(s >= tmp.length())
|
||||||
|
throw std::runtime_error("Port number has to be specified");
|
||||||
|
node = tmp.substr(0, s);
|
||||||
|
service = tmp.substr(s + 1);
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
#ifdef AI_V4MAPPED
|
||||||
|
hints.ai_flags = AI_V4MAPPED;
|
||||||
|
#endif
|
||||||
|
#ifdef AI_ADDRCONFIG
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
|
#endif
|
||||||
|
real = true;
|
||||||
|
r = getaddrinfo(node.c_str(), service.c_str(), &hints, &ainfo);
|
||||||
|
if(r < 0)
|
||||||
|
throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(r));
|
||||||
|
establish:
|
||||||
|
family = ainfo->ai_family;
|
||||||
|
socktype = ainfo->ai_socktype;
|
||||||
|
protocol = ainfo->ai_protocol;
|
||||||
|
try {
|
||||||
|
memory.resize(ainfo->ai_addrlen);
|
||||||
|
memcpy(&memory[0], ainfo->ai_addr, ainfo->ai_addrlen);
|
||||||
|
} catch(...) {
|
||||||
|
freeaddrinfo(ainfo);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
freeaddrinfo(ainfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_address socket_address::next()
|
||||||
|
{
|
||||||
|
std::vector<char> newaddr = memory;
|
||||||
|
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&newaddr[0]);
|
||||||
|
socklen_t addrlen = memory.size();
|
||||||
|
switch(addr->sa_family) {
|
||||||
|
case AF_INET: { //IPv4
|
||||||
|
struct sockaddr_in* _addr = (struct sockaddr_in*)addr;
|
||||||
|
_addr->sin_port = htons(htons(_addr->sin_port) + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AF_INET6: { //IPv6
|
||||||
|
struct sockaddr_in6* _addr = (struct sockaddr_in6*)addr;
|
||||||
|
_addr->sin6_port = htons(htons(_addr->sin6_port) + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AF_UNIX: { //Unix domain sockets.
|
||||||
|
struct sockaddr_un* _addr = (struct sockaddr_un*)addr;
|
||||||
|
const char* b1 = (char*)_addr;
|
||||||
|
const char* b2 = (char*)&_addr->sun_path;
|
||||||
|
size_t maxpath = addrlen - (b2 - b1);
|
||||||
|
for(size_t i = 0; i < maxpath; i++)
|
||||||
|
if(i && !_addr->sun_path[i]) {
|
||||||
|
maxpath = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!maxpath)
|
||||||
|
throw std::runtime_error("Eh, empty unix domain socket path?");
|
||||||
|
_addr->sun_path[maxpath - 1]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("This address family is not supported, sorry.");
|
||||||
|
}
|
||||||
|
socket_address n(family, socktype, protocol);
|
||||||
|
n.memory = newaddr;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& socket_address::connect()
|
||||||
|
{
|
||||||
|
int a = socket(family, socktype, protocol);
|
||||||
|
if(a < 0) {
|
||||||
|
int err = errno;
|
||||||
|
throw std::runtime_error(std::string("socket: ") + strerror(err));
|
||||||
|
}
|
||||||
|
int r;
|
||||||
|
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&memory[0]);
|
||||||
|
socklen_t addrlen = memory.size();
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
r = ::connect(a, addr, addrlen) ? -1 : 0;
|
||||||
|
#else
|
||||||
|
r = ::connect(a, addr, addrlen);
|
||||||
|
#endif
|
||||||
|
if(r < 0) {
|
||||||
|
int err = errno;
|
||||||
|
::close(a);
|
||||||
|
throw std::runtime_error(std::string("connect: ") + strerror(err));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return *new boost::iostreams::stream<socket_output>(a);
|
||||||
|
} catch(...) {
|
||||||
|
::close(a);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool socket_address::supported()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_address::socket_address(int f, int st, int p)
|
||||||
|
{
|
||||||
|
family = f;
|
||||||
|
socktype = st;
|
||||||
|
protocol = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue