Also support dumping JMD and SDMP over TCP/IP
[lsnes.git] / src / video / tcp.cpp
blob14397e83df6595fe1276c0f461c7b6ea237c49ef
1 #include "video/tcp.hpp"
3 #ifdef NO_TCP_SOCKETS
5 namespace
7 void deleter_fn(void* f)
12 socket_address::socket_address(const std::string& spec)
14 throw std::runtime_error("TCP/IP support not compiled in");
17 socket_address socket_address::next()
21 std::ostream& socket_address::connect()
23 throw std::runtime_error("TCP/IP support not compiled in");
26 bool socket_address::supported()
28 return false;
31 #else
32 #include <boost/iostreams/categories.hpp>
33 #include <boost/iostreams/copy.hpp>
34 #include <boost/iostreams/stream.hpp>
35 #include <boost/iostreams/stream_buffer.hpp>
36 #include <boost/iostreams/filter/symmetric.hpp>
37 #include <boost/iostreams/filter/zlib.hpp>
38 #include <boost/iostreams/filtering_stream.hpp>
39 #include <boost/iostreams/device/back_inserter.hpp>
40 #include <unistd.h>
41 #if defined(_WIN32) || defined(_WIN64)
42 //Why the fuck does windows have nonstandard socket API???
43 #define _WIN32_WINNT 0x0501
44 #include <winsock2.h>
45 #include <ws2tcpip.h>
46 struct sockaddr_un { int sun_family; char sun_path[108]; };
47 #else
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #include <sys/un.h>
51 #endif
52 #include <sys/types.h>
54 namespace
56 class socket_output
58 public:
59 typedef char char_type;
60 typedef struct : public boost::iostreams::sink_tag, boost::iostreams::closable_tag {} category;
61 socket_output(int _fd)
62 : fd(_fd)
64 broken = false;
67 void close()
69 ::close(fd);
72 std::streamsize write(const char* s, std::streamsize n)
74 if(broken)
75 return n;
76 size_t w = n;
77 while(n > 0) {
78 ssize_t r = ::send(fd, s, n, 0);
79 if(r >= 0) {
80 s += r;
81 n -= r;
82 } else if(errno == EPIPE) {
83 std::cerr << "The other end of socket went away" << std::endl;
84 broken = true;
85 n = 0;
86 break;
87 } else { //Error.
88 int err = errno;
89 std::cerr << "Socket write error: " << strerror(err) << std::endl;
90 n = 0;
91 break;
94 return w;
96 protected:
97 int fd;
98 bool broken;
101 void deleter_fn(void* f)
103 delete reinterpret_cast<boost::iostreams::stream<socket_output>*>(f);
107 socket_address::socket_address(const std::string& name)
109 struct addrinfo hints;
110 struct addrinfo* ainfo;
111 bool real = false;
112 int r;
113 std::string node, service, tmp = name;
114 size_t s;
115 struct sockaddr_un uaddr;
116 if(name[0] == '/' || name[0] == '@') {
117 //Fake a unix-domain.
118 if(name.length() >= sizeof(sockaddr_un) - offsetof(sockaddr_un, sun_path) - 1)
119 throw std::runtime_error("Path too long for filesystem socket");
120 size_t namelen = offsetof(struct sockaddr_un, sun_path) + name.length();
121 uaddr.sun_family = AF_UNIX;
122 strcpy(uaddr.sun_path, name.c_str());
123 if(name[0] == '@')
124 uaddr.sun_path[0] = 0; //Mark as abstract namespace socket.
125 family = AF_UNIX;
126 socktype = SOCK_STREAM;
127 protocol = 0;
128 memory.resize((name[0] == '@') ? namelen : sizeof(sockaddr_un));
129 memcpy(&memory[0], &uaddr, memory.size());
130 return;
132 //Split into address and port.
133 s = tmp.find_last_of(":");
134 if(s >= tmp.length())
135 throw std::runtime_error("Port number has to be specified");
136 node = tmp.substr(0, s);
137 service = tmp.substr(s + 1);
139 memset(&hints, 0, sizeof(hints));
140 hints.ai_family = AF_UNSPEC;
141 hints.ai_socktype = SOCK_STREAM;
142 #ifdef AI_V4MAPPED
143 hints.ai_flags = AI_V4MAPPED;
144 #endif
145 #ifdef AI_ADDRCONFIG
146 hints.ai_flags = AI_ADDRCONFIG;
147 #endif
148 real = true;
149 r = getaddrinfo(node.c_str(), service.c_str(), &hints, &ainfo);
150 if(r < 0)
151 throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(r));
152 establish:
153 family = ainfo->ai_family;
154 socktype = ainfo->ai_socktype;
155 protocol = ainfo->ai_protocol;
156 try {
157 memory.resize(ainfo->ai_addrlen);
158 memcpy(&memory[0], ainfo->ai_addr, ainfo->ai_addrlen);
159 } catch(...) {
160 freeaddrinfo(ainfo);
161 throw;
163 freeaddrinfo(ainfo);
166 socket_address socket_address::next()
168 std::vector<char> newaddr = memory;
169 struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&newaddr[0]);
170 socklen_t addrlen = memory.size();
171 switch(addr->sa_family) {
172 case AF_INET: { //IPv4
173 struct sockaddr_in* _addr = (struct sockaddr_in*)addr;
174 _addr->sin_port = htons(htons(_addr->sin_port) + 1);
175 break;
177 case AF_INET6: { //IPv6
178 struct sockaddr_in6* _addr = (struct sockaddr_in6*)addr;
179 _addr->sin6_port = htons(htons(_addr->sin6_port) + 1);
180 break;
182 case AF_UNIX: { //Unix domain sockets.
183 struct sockaddr_un* _addr = (struct sockaddr_un*)addr;
184 const char* b1 = (char*)_addr;
185 const char* b2 = (char*)&_addr->sun_path;
186 size_t maxpath = addrlen - (b2 - b1);
187 for(size_t i = 0; i < maxpath; i++)
188 if(i && !_addr->sun_path[i]) {
189 maxpath = i;
190 break;
192 if(!maxpath)
193 throw std::runtime_error("Eh, empty unix domain socket path?");
194 _addr->sun_path[maxpath - 1]++;
195 break;
197 default:
198 throw std::runtime_error("This address family is not supported, sorry.");
200 socket_address n(family, socktype, protocol);
201 n.memory = newaddr;
202 return n;
205 std::ostream& socket_address::connect()
207 int a = socket(family, socktype, protocol);
208 if(a < 0) {
209 int err = errno;
210 throw std::runtime_error(std::string("socket: ") + strerror(err));
212 int r;
213 struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&memory[0]);
214 socklen_t addrlen = memory.size();
215 #if defined(_WIN32) || defined(_WIN64)
216 r = ::connect(a, addr, addrlen) ? -1 : 0;
217 #else
218 r = ::connect(a, addr, addrlen);
219 #endif
220 if(r < 0) {
221 int err = errno;
222 ::close(a);
223 throw std::runtime_error(std::string("connect: ") + strerror(err));
225 try {
226 return *new boost::iostreams::stream<socket_output>(a);
227 } catch(...) {
228 ::close(a);
229 throw;
233 bool socket_address::supported()
235 return true;
238 #endif
240 deleter_fn_t socket_address::deleter()
242 return deleter_fn;
246 socket_address::socket_address(int f, int st, int p)
248 family = f;
249 socktype = st;
250 protocol = p;