FS2_Open
Open source remastering of the Freespace 2 engine
tcp_socket.cpp
Go to the documentation of this file.
1 // TCP_Socket.cpp
2 // TCP_Socket for FS2Open PXO
3 // Derek Meek
4 // 2-14-2003
5 
6 // ############## ATTENTION ##########
7 // Licensed under the Academic Free License version 2.0
8 // View License at http://www.opensource.org/licenses/afl-2.0.php
9 // ###################################
10 
11 
12 
13 
14 #include "fs2netd/tcp_socket.h"
15 #include "globalincs/pstypes.h"
16 #include "network/multi_log.h"
17 
18 
19 #ifdef SCP_UNIX
20 #include <sys/time.h> // The OS X 10.3 SDK appears to need this for some reason (for timeval struct)
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <netdb.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <cerrno>
29 #include <sys/ioctl.h>
30 #ifdef SCP_SOLARIS
31 #include <sys/filio.h>
32 #endif
33 #include <ctype.h>
34 
35 #define WSAGetLastError() (errno)
36 #else
37 #include <windows.h>
38 #include <process.h>
39 typedef int socklen_t;
40 #endif
41 
42 
43 static bool Connecting = false;
44 static bool Connected = false;
45 static SOCKET mySocket = INVALID_SOCKET;
46 
47 static sockaddr_in FS2NetD_addr;
48 
49 
51 {
52  if (mySocket != INVALID_SOCKET) {
53  shutdown(mySocket, 2);
54  closesocket(mySocket);
55  mySocket = INVALID_SOCKET;
56  }
57 
58  Connected = false;
59  Connecting = false;
60 }
61 
62 int FS2NetD_ConnectToServer(const char *host, const char *port)
63 {
64  struct hostent *my_host = NULL;
65  char host_str[254]; // 253 is max domain name length: http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
66 #ifdef SCP_UNIX
67  int my_error = 0;
68 #endif
69 
70  if ( !Connecting ) {
71 #ifdef WIN32
72  WSADATA wsa;
73  int rc = WSAStartup(MAKEWORD(2,0), &wsa);
74 
75  if (rc != 0) {
76  ml_printf("FS2NetD ERROR: Failed to start WinSock!");
77  return -1;
78  }
79 #endif
80 
81  memset(&FS2NetD_addr, 0, sizeof(sockaddr_in));
82  FS2NetD_addr.sin_family = AF_INET;
83  FS2NetD_addr.sin_addr.s_addr = INADDR_ANY;
84  FS2NetD_addr.sin_port = 0;
85 
86  mySocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
87 
88  if (mySocket == SOCKET_ERROR) {
89 #ifdef SCP_UNIX
90  my_error = errno;
91  ml_printf("FS2NetD ERROR: Couldn't get socket (\"%s\")!", strerror(my_error));
92 #else
93  ml_printf("FS2NetD ERROR: Couldn't get socket!");
94 #endif
95  return -1;
96  }
97 
98  if ( bind(mySocket, (sockaddr *)&FS2NetD_addr, sizeof(sockaddr)) == SOCKET_ERROR ) {
99 #ifdef SCP_UNIX
100  my_error = errno;
101  ml_printf("FS2NetD ERROR: Couldn't bind socket (\"%s\")!", strerror(my_error));
102 #else
103  ml_printf("FS2NetD ERROR: Couldn't bind socket!");
104 #endif
105  return -1;
106  }
107 
108  // set to non-blocking mode
109  ulong arg = 1;
110  ioctlsocket(mySocket, FIONBIO, &arg);
111 
112  // blasted MS, probably need to use getaddrinfo() here for Win32, but I
113  // want to keep this as clean and simple as possible and that means
114  // using an actual standard :)
115 
116  strcpy_s( host_str, host );
117 
118  // check that we aren't in a dotted format, some gethostbyname() implementations don't like that
119  // (NOTE: Yes, I'm aware that this is problematic if a host name uses an initial digit)
120  if ( !isdigit(host_str[0]) ) {
121  my_host = gethostbyname( host );
122 
123  if (my_host == NULL) {
124  // NOTE: that we don't do specific error reporting here since it's totally different
125  // on Win32, compared to everything else that is actually standard
126  ml_printf("Failure from gethostbyname() for host '%s'", host);
127  return -1;
128  } else if (my_host->h_addrtype != AF_INET) {
129  ml_printf("Invalid address type returned by gethostbyname()!");
130  return -1;
131  } else if (my_host->h_addr_list[0] == NULL) {
132  ml_printf("Unable to determine IP from host name '%s'", host);
133  return -1;
134  }
135 
136  FS2NetD_addr.sin_addr.s_addr = ((in_addr *)(my_host->h_addr_list[0]))->s_addr;
137  }
138  // we might be in dotted format so try using it as such
139  else {
140  FS2NetD_addr.sin_addr.s_addr = inet_addr( host );
141  }
142 
143  if (FS2NetD_addr.sin_addr.s_addr == INADDR_ANY) {
144  ml_printf("No valid server address to connect with!");
145  return -1;
146  }
147 
148  // we need to set the correct port before moving on
149  long m_port = strtol(port, (char**)NULL, 10);
150  FS2NetD_addr.sin_port = htons( (ushort)m_port );
151 
152 
153  if ( connect(mySocket, (sockaddr *)&FS2NetD_addr, sizeof(sockaddr)) == SOCKET_ERROR ) {
154  if ( WSAGetLastError() == WSAEWOULDBLOCK ) {
155  Connecting = true;
156  return 0;
157  } else {
158 #ifdef SCP_UNIX
159  int errv = errno;
160  ml_printf("FS2NetD ERROR: Couldn't connect to remote system at %s (\"%s\")!", inet_ntoa(FS2NetD_addr.sin_addr), strerror(errv));
161 #else
162  ml_printf("FS2NetD ERROR: Couldn't connect to remote system at %s!", inet_ntoa(FS2NetD_addr.sin_addr));
163 #endif
164  }
165 
166  return -1;
167  }
168  // technically there should always be an error since we should be non-blocking
169  else {
170  Connecting = true;
171  Connected = true;
172 
173  return 1;
174  }
175  }
176  // done starting the connection, so lets see if we are actually connected yet
177  else {
178  if (Connected) {
179  Int3();
180  return 1;
181  }
182 
183  fd_set write_fds, error_fds;
184  timeval timeout;
185 
186  timeout.tv_sec = 0;
187  timeout.tv_usec = 1;
188 
189  FD_ZERO(&write_fds);
190  FD_SET(mySocket, &write_fds);
191 
192  // if it's writeable then we are fully connected
193  if ( select(mySocket+1, NULL, &write_fds, NULL, &timeout) > 0 ) {
194  // make sure that we don't have any connect() errors (since it's non-blocking)
195  int err_val = 0;
196  size_t err_val_size = sizeof(err_val);
197  getsockopt(mySocket, SOL_SOCKET, SO_ERROR, (char*)&err_val, (socklen_t*)&err_val_size);
198 
199  if (err_val) {
200  // if we aren't still in blocking mode then we can't connect
201  if (err_val != WSAEWOULDBLOCK) {
202  return -1;
203  }
204 
205  return 0;
206  }
207 
208  Connected = true;
209  return 1;
210  }
211 
212  FD_ZERO(&error_fds);
213  FD_SET(mySocket, &error_fds);
214 
215  // if it's in error then it has failed to connect
216  if ( select(mySocket+1, NULL, NULL, &error_fds, &timeout) )
217  return -1;
218 
219  // not connected, and haven't failed to connect, so keep in the loop
220  return 0;
221  }
222 }
223 
224 int FS2NetD_GetData(char *buffer, int blen)
225 {
226  int flags = 0;
227 
228  // clear the buffer
229  memset(buffer, 0, blen);
230 
231  socklen_t from_len = sizeof(sockaddr);
232 
233  return recvfrom(mySocket, buffer, blen, flags, (sockaddr*)&FS2NetD_addr, &from_len);
234 }
235 
236 int FS2NetD_SendData(char *buffer, int blen)
237 {
238  int flags = 0;
239 
240  socklen_t to_len = sizeof(sockaddr);
241 
242  return sendto(mySocket, buffer, blen, flags, (sockaddr*)&FS2NetD_addr, to_len);
243 }
244 
246 {
247  timeval wait;
248  wait.tv_sec = 0;
249  wait.tv_usec = 1;
250 
251  fd_set recvs;
252 
253  FD_ZERO(&recvs);
254  FD_SET(mySocket, &recvs);
255 
256 #ifndef SCP_UNIX
257  int status = select(1, &recvs, NULL, NULL, &wait);
258 #else
259  int status = select(mySocket+1, &recvs, NULL, NULL, &wait);
260 #endif
261 
262  return ( (status != 0) && (status != -1) && FD_ISSET(mySocket, &recvs) );
263 }
#define WSAEWOULDBLOCK
Definition: config.h:131
int FS2NetD_SendData(char *buffer, int blen)
Definition: tcp_socket.cpp:236
void FS2NetD_Disconnect()
Definition: tcp_socket.cpp:50
int FS2NetD_ConnectToServer(const char *host, const char *port)
Definition: tcp_socket.cpp:62
bool FS2NetD_DataReady()
Definition: tcp_socket.cpp:245
#define WSAGetLastError()
Definition: cftp.cpp:29
#define SOCKET_ERROR
Definition: config.h:138
#define INVALID_SOCKET
Definition: psnet2.h:53
#define Int3()
Definition: pstypes.h:292
int FS2NetD_GetData(char *buffer, int blen)
Definition: tcp_socket.cpp:224
void ml_printf(const char *format,...)
Definition: multi_log.cpp:112
net_player * host
Definition: multi.h:504
GLuint buffer
Definition: Glext.h:5492
#define SOCKET
Definition: config.h:122
unsigned long ulong
Definition: pstypes.h:65
GLbitfield GLuint64 timeout
Definition: Glext.h:6725
GLbitfield flags
Definition: Glext.h:6722
#define closesocket(x)
Definition: config.h:128
unsigned short ushort
Definition: pstypes.h:63
#define ioctlsocket(x, y, z)
Definition: config.h:139
int socklen_t
Definition: tcp_socket.cpp:39
#define strcpy_s(...)
Definition: safe_strings.h:67