91b2ef73a717ecf78a522177b5a38ebf9be2869a
[oweals/minetest.git] / src / socket.cpp
1 #include "socket.h"
2 #include "debug.h"
3 #include <stdio.h>
4 #include <iostream>
5 #include <stdlib.h>
6 #include <errno.h>
7
8 // Debug printing options
9 #define DP 0
10 #define DPS ""
11
12 bool g_sockets_initialized = false;
13
14 void sockets_init()
15 {
16 #ifdef _WIN32
17         WSADATA WsaData;
18         if(WSAStartup( MAKEWORD(2,2), &WsaData ) != NO_ERROR)
19                 throw SocketException("WSAStartup failed");
20 #else
21 #endif
22         g_sockets_initialized = true;
23 }
24
25 void sockets_cleanup()
26 {
27 #ifdef _WIN32
28         WSACleanup();
29 #endif
30 }
31
32 Address::Address()
33 {
34 }
35
36 Address::Address(unsigned int address, unsigned short port)
37 {
38         m_address = address;
39         m_port = port;
40 }
41
42 Address::Address(unsigned int a, unsigned int b,
43                 unsigned int c, unsigned int d,
44                 unsigned short port)
45 {
46         m_address = (a<<24) | (b<<16) | ( c<<8) | d;
47         m_port = port;
48 }
49
50 bool Address::operator==(Address &address)
51 {
52         return (m_address == address.m_address
53                         && m_port == address.m_port);
54 }
55
56 bool Address::operator!=(Address &address)
57 {
58         return !(*this == address);
59 }
60
61 void Address::Resolve(const char *name)
62 {
63         struct addrinfo *resolved;
64         int e = getaddrinfo(name, NULL, NULL, &resolved);
65         if(e != 0)
66                 throw ResolveError("");
67         /*
68                 FIXME: This is an ugly hack; change the whole class
69                 to store the address as sockaddr
70         */
71         struct sockaddr_in *t = (struct sockaddr_in*)resolved->ai_addr;
72         m_address = ntohl(t->sin_addr.s_addr);
73         freeaddrinfo(resolved);
74 }
75
76 unsigned int Address::getAddress() const
77 {
78         return m_address;
79 }
80
81 unsigned short Address::getPort() const
82 {
83         return m_port;
84 }
85
86 void Address::setAddress(unsigned int address)
87 {
88         m_address = address;
89 }
90
91 void Address::setPort(unsigned short port)
92 {
93         m_port = port;
94 }
95
96 void Address::print(std::ostream *s) const
97 {
98         (*s)<<((m_address>>24)&0xff)<<"."
99                         <<((m_address>>16)&0xff)<<"."
100                         <<((m_address>>8)&0xff)<<"."
101                         <<((m_address>>0)&0xff)<<":"
102                         <<m_port;
103 }
104
105 void Address::print() const
106 {
107         print(&dstream);
108 }
109
110 UDPSocket::UDPSocket()
111 {
112         if(g_sockets_initialized == false)
113                 throw SocketException("Sockets not initialized");
114         
115     m_handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
116         
117         if(DP)
118         dstream<<DPS<<"UDPSocket("<<(int)m_handle<<")::UDPSocket()"<<std::endl;
119         
120     if(m_handle <= 0)
121     {
122                 throw SocketException("Failed to create socket");
123     }
124
125 /*#ifdef _WIN32
126         DWORD nonblocking = 0;
127         if(ioctlsocket(m_handle, FIONBIO, &nonblocking) != 0)
128         {
129                 throw SocketException("Failed set non-blocking mode");
130         }
131 #else
132         int nonblocking = 0;
133         if(fcntl(m_handle, F_SETFL, O_NONBLOCK, nonblocking) == -1)
134         {
135                 throw SocketException("Failed set non-blocking mode");
136         }
137 #endif*/
138
139         setTimeoutMs(0);
140 }
141
142 UDPSocket::~UDPSocket()
143 {
144         if(DP)
145         dstream<<DPS<<"UDPSocket("<<(int)m_handle<<")::~UDPSocket()"<<std::endl;
146
147 #ifdef _WIN32
148         closesocket(m_handle);
149 #else
150         close(m_handle);
151 #endif
152 }
153
154 void UDPSocket::Bind(unsigned short port)
155 {
156         if(DP)
157         dstream<<DPS<<"UDPSocket("<<(int)m_handle
158                         <<")::Bind(): port="<<port<<std::endl;
159
160     sockaddr_in address;
161     address.sin_family = AF_INET;
162     address.sin_addr.s_addr = INADDR_ANY;
163     address.sin_port = htons(port);
164
165     if(bind(m_handle, (const sockaddr*)&address, sizeof(sockaddr_in)) < 0)
166     {
167                 dstream<<(int)m_handle<<": Bind failed: "<<strerror(errno)<<std::endl;
168                 throw SocketException("Failed to bind socket");
169     }
170 }
171
172 void UDPSocket::Send(const Address & destination, const void * data, int size)
173 {
174         bool dumping_packet = false;
175         if(INTERNET_SIMULATOR)
176                 dumping_packet = (rand()%10==0); //easy
177                 //dumping_packet = (rand()%4==0); // hard
178
179         if(DP){
180                 /*dstream<<DPS<<"UDPSocket("<<(int)m_handle
181                                 <<")::Send(): destination=";*/
182                 dstream<<DPS;
183                 dstream<<(int)m_handle<<" -> ";
184                 destination.print();
185                 dstream<<", size="<<size<<", data=";
186                 for(int i=0; i<size && i<20; i++){
187                         if(i%2==0) printf(" ");
188                         DEBUGPRINT("%.2X", ((int)((const char*)data)[i])&0xff);
189                 }
190                 if(size>20)
191                         dstream<<"...";
192                 if(dumping_packet)
193                         dstream<<" (DUMPED BY INTERNET_SIMULATOR)";
194                 dstream<<std::endl;
195         }
196         else if(dumping_packet)
197         {
198                 // Lol let's forget it
199                 dstream<<"UDPSocket::Send(): "
200                                 "INTERNET_SIMULATOR: dumping packet."
201                                 <<std::endl;
202         }
203
204         if(dumping_packet)
205                 return;
206
207         sockaddr_in address;
208         address.sin_family = AF_INET;
209         address.sin_addr.s_addr = htonl(destination.getAddress());
210         address.sin_port = htons(destination.getPort());
211
212         int sent = sendto(m_handle, (const char*)data, size,
213                 0, (sockaddr*)&address, sizeof(sockaddr_in));
214
215     if(sent != size)
216     {
217                 throw SendFailedException("Failed to send packet");
218     }
219 }
220
221 int UDPSocket::Receive(Address & sender, void * data, int size)
222 {
223         if(WaitData(m_timeout_ms) == false)
224         {
225                 return -1;
226         }
227
228         sockaddr_in address;
229         socklen_t address_len = sizeof(address);
230
231         int received = recvfrom(m_handle, (char*)data,
232                         size, 0, (sockaddr*)&address, &address_len);
233
234         if(received < 0)
235                 return -1;
236
237         unsigned int address_ip = ntohl(address.sin_addr.s_addr);
238         unsigned int address_port = ntohs(address.sin_port);
239
240         sender = Address(address_ip, address_port);
241
242         if(DP){
243                 //dstream<<DPS<<"UDPSocket("<<(int)m_handle<<")::Receive(): sender=";
244                 dstream<<DPS<<(int)m_handle<<" <- ";
245                 sender.print();
246                 //dstream<<", received="<<received<<std::endl;
247                 dstream<<", size="<<received<<", data=";
248                 for(int i=0; i<received && i<20; i++){
249                         if(i%2==0) printf(" ");
250                         DEBUGPRINT("%.2X", ((int)((const char*)data)[i])&0xff);
251                 }
252                 if(received>20)
253                         dstream<<"...";
254                 dstream<<std::endl;
255         }
256
257         return received;
258 }
259
260 int UDPSocket::GetHandle()
261 {
262         return m_handle;
263 }
264
265 void UDPSocket::setTimeoutMs(int timeout_ms)
266 {
267         m_timeout_ms = timeout_ms;
268 }
269
270 bool UDPSocket::WaitData(int timeout_ms)
271 {
272         fd_set readset;
273         int result;
274
275         // Initialize the set
276         FD_ZERO(&readset);
277         FD_SET(m_handle, &readset);
278
279         // Initialize time out struct
280         struct timeval tv;
281         tv.tv_sec = 0;
282         tv.tv_usec = timeout_ms * 1000;
283         // select()
284         result = select(m_handle+1, &readset, NULL, NULL, &tv);
285
286         if(result == 0){
287                 // Timeout
288                 /*dstream<<"Select timed out (timeout_ms="
289                                 <<timeout_ms<<")"<<std::endl;*/
290                 return false;
291         }
292         else if(result < 0){
293                 // Error
294                 dstream<<(int)m_handle<<": Select failed: "<<strerror(errno)<<std::endl;
295 #ifdef _WIN32
296                 dstream<<(int)m_handle<<": WSAGetLastError()="<<WSAGetLastError()<<std::endl;
297 #endif
298                 throw SocketException("Select failed");
299         }
300         else if(FD_ISSET(m_handle, &readset) == false){
301                 // No data
302                 //dstream<<"Select reported no data in m_handle"<<std::endl;
303                 return false;
304         }
305         
306         // There is data
307         //dstream<<"Select reported data in m_handle"<<std::endl;
308         return true;
309 }
310
311