Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / video / tcp.cpp
blob2b21420ca09ae70a6b9845a091a84ca5579b623a
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 int r;
112 std::string node, service, tmp = name;
113 size_t s;
114 struct sockaddr_un uaddr;
115 if(name[0] == '/' || name[0] == '@') {
116 //Fake a unix-domain.
117 if(name.length() >= sizeof(sockaddr_un) - offsetof(sockaddr_un, sun_path) - 1)
118 throw std::runtime_error("Path too long for filesystem socket");
119 size_t namelen = offsetof(struct sockaddr_un, sun_path) + name.length();
120 uaddr.sun_family = AF_UNIX;
121 strcpy(uaddr.sun_path, name.c_str());
122 if(name[0] == '@')
123 uaddr.sun_path[0] = 0; //Mark as abstract namespace socket.
124 family = AF_UNIX;
125 socktype = SOCK_STREAM;
126 protocol = 0;
127 memory.resize((name[0] == '@') ? namelen : sizeof(sockaddr_un));
128 memcpy(&memory[0], &uaddr, memory.size());
129 return;
131 //Split into address and port.
132 s = tmp.find_last_of(":");
133 if(s >= tmp.length())
134 throw std::runtime_error("Port number has to be specified");
135 node = tmp.substr(0, s);
136 service = tmp.substr(s + 1);
138 memset(&hints, 0, sizeof(hints));
139 hints.ai_family = AF_UNSPEC;
140 hints.ai_socktype = SOCK_STREAM;
141 #ifdef AI_V4MAPPED
142 hints.ai_flags = AI_V4MAPPED;
143 #endif
144 #ifdef AI_ADDRCONFIG
145 hints.ai_flags = AI_ADDRCONFIG;
146 #endif
147 r = getaddrinfo(node.c_str(), service.c_str(), &hints, &ainfo);
148 if(r < 0)
149 throw std::runtime_error(std::string("getaddrinfo: ") + gai_strerror(r));
150 family = ainfo->ai_family;
151 socktype = ainfo->ai_socktype;
152 protocol = ainfo->ai_protocol;
153 try {
154 memory.resize(ainfo->ai_addrlen);
155 memcpy(&memory[0], ainfo->ai_addr, ainfo->ai_addrlen);
156 } catch(...) {
157 freeaddrinfo(ainfo);
158 throw;
160 freeaddrinfo(ainfo);
163 socket_address socket_address::next()
165 std::vector<char> newaddr = memory;
166 struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&newaddr[0]);
167 socklen_t addrlen = memory.size();
168 switch(addr->sa_family) {
169 case AF_INET: { //IPv4
170 struct sockaddr_in* _addr = (struct sockaddr_in*)addr;
171 _addr->sin_port = htons(htons(_addr->sin_port) + 1);
172 break;
174 case AF_INET6: { //IPv6
175 struct sockaddr_in6* _addr = (struct sockaddr_in6*)addr;
176 _addr->sin6_port = htons(htons(_addr->sin6_port) + 1);
177 break;
179 case AF_UNIX: { //Unix domain sockets.
180 struct sockaddr_un* _addr = (struct sockaddr_un*)addr;
181 const char* b1 = (char*)_addr;
182 const char* b2 = (char*)&_addr->sun_path;
183 size_t maxpath = addrlen - (b2 - b1);
184 for(size_t i = 0; i < maxpath; i++)
185 if(i && !_addr->sun_path[i]) {
186 maxpath = i;
187 break;
189 if(!maxpath)
190 throw std::runtime_error("Eh, empty unix domain socket path?");
191 _addr->sun_path[maxpath - 1]++;
192 break;
194 default:
195 throw std::runtime_error("This address family is not supported, sorry.");
197 socket_address n(family, socktype, protocol);
198 n.memory = newaddr;
199 return n;
202 std::ostream& socket_address::connect()
204 int a = socket(family, socktype, protocol);
205 if(a < 0) {
206 int err = errno;
207 throw std::runtime_error(std::string("socket: ") + strerror(err));
209 int r;
210 struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&memory[0]);
211 socklen_t addrlen = memory.size();
212 #if defined(_WIN32) || defined(_WIN64)
213 r = ::connect(a, addr, addrlen) ? -1 : 0;
214 #else
215 r = ::connect(a, addr, addrlen);
216 #endif
217 if(r < 0) {
218 int err = errno;
219 ::close(a);
220 throw std::runtime_error(std::string("connect: ") + strerror(err));
222 try {
223 return *new boost::iostreams::stream<socket_output>(a);
224 } catch(...) {
225 ::close(a);
226 throw;
230 bool socket_address::supported()
232 return true;
235 #endif
237 deleter_fn_t socket_address::deleter()
239 return deleter_fn;
243 socket_address::socket_address(int f, int st, int p)
245 family = f;
246 socktype = st;
247 protocol = p;