feisty meow concerns codebase  2.140
raw_socket.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : raw_socket *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 1991-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "raw_socket.h"
16 #include "tcpip_stack.h"
17 
18 #include <basis/functions.h>
21 #include <timely/time_stamp.h>
22 
23 #include <stdlib.h>
24 #ifdef __APPLE__
25  #include <fcntl.h>
26 #endif
27 //#ifdef __UNIX__
28  #include <arpa/inet.h>
29  #include <errno.h>
30  #include <netinet/tcp.h>
31  #include <sys/ioctl.h>
32  #include <sys/socket.h>
33  #include <unistd.h>
34  #define OPTYPE (void *)
35 //#endif
36 //#ifdef __WIN32__
37 // #define OPTYPE (char *)
38 //#endif
39 
40 using namespace basis;
41 using namespace loggers;
42 using namespace timely;
43 
44 namespace sockets {
45 
46 //#define DEBUG_RAW_SOCKET
47  // uncomment for noisy diagnostics.
48 
49 #undef LOG
50 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
51 
53  // we will make certain that select really says there's data and ioctl
54  // really says there's no data waiting before we believe there's been
55  // a disconnect. we'll check that state the number of times specified.
56 
57 class fd_set_wrapper : public fd_set {};
58 
60 
61 const basis::un_int NON_BLOCKING = FIONBIO;
62 const basis::un_int IOCTL_READ = FIONREAD;
63 
64 /*
65 #ifdef __WIN32__
66 // defined by winsock header but not present in the winsock dll.
67 int PASCAL FAR __WSAFDIsSet(SOCKET fd, fd_set FAR *the_set)
68 {
69  int i = the_set->fd_count;
70  while (i--)
71  if (the_set->fd_array[i] == fd)
72  return true;
73  return false;
74 }
75 #endif
76 */
77 
79 
80 raw_socket::raw_socket()
81 : _stack(new tcpip_stack())
82 {}
83 
85 {
86  WHACK(_stack);
87 }
88 
90 {
91  int to_return = 0;
92 //#ifdef __WIN32__
93 // to_return = closesocket(socket);
94 //#endif
95 //#ifdef __UNIX__
96  to_return = ::close(socket);
97 //#endif
98  socket = 0;
99  return to_return;
100 }
101 
102 //move this into parser bits as a OR combiner or something.
103 void combine(astring &existing, const astring &addition)
104 {
105  if (addition.t()) {
106  if (existing.t()) existing += " | ";
107  existing += addition;
108  }
109 }
110 
112 {
113  astring to_return;
114  if (interest & SI_CONNECTED) combine(to_return, "CONNECTED");
115  if (interest & SI_DISCONNECTED) combine(to_return, "DISCONNECTED");
116  if (interest & SI_WRITABLE) combine(to_return, "WRITABLE");
117  if (interest & SI_READABLE) combine(to_return, "READABLE");
118  if (interest & SI_ERRONEOUS) combine(to_return, "ERRONEOUS");
119  if (!interest) combine(to_return, "NORMAL");
120  return to_return;
121 }
122 
123 int raw_socket::ioctl(basis::un_int socket, int request, void *argp) const
124 {
125 //#ifdef __UNIX__
126  return ::ioctl(socket, request, argp);
127  /*
128 #endif
129 #ifdef __WIN32__
130  #ifdef _MSC_VER
131  return ioctlsocket(socket, request, (un_long *)argp);
132  #else
133  return ioctlsocket(socket, request, (un_int *)argp);
134  #endif
135 #endif
136 */
137 }
138 
139 bool raw_socket::set_non_blocking(basis::un_int socket, bool non_blocking)
140 {
141  FUNCDEF("set_non_blocking");
142 #ifdef __APPLE__
143  int curr_flags = fcntl(socket, F_GETFL, 0);
144  if (fcntl(socket, F_SETFL, curr_flags | O_NONBLOCK) < 0) return false;
145 #else
146  int arg = int(non_blocking);
147  if (negative(ioctl(socket, NON_BLOCKING, &arg))) {
148  LOG(a_sprintf("Could not set non-blocking (FIONBIO) option on raw_socket %u.", socket));
149  return false;
150  }
151 #endif
152  return true;
153 }
154 
155 bool raw_socket::set_nagle_algorithm(basis::un_int socket, bool use_nagle)
156 {
157  FUNCDEF("set_nagle_algorithm");
158  int arg = int(!use_nagle); // opposite of the flag, since we set no-delay.
159  if (negative(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, OPTYPE &arg,
160  sizeof(arg)))) {
161  LOG(a_sprintf("Could not change nagle coalescing mode on %u.", socket));
162  return false;
163  }
164  return true;
165 }
166 
167 bool raw_socket::set_broadcast(basis::un_int socket, bool broadcasting)
168 {
169  FUNCDEF("set_broadcast");
170  int arg = int(broadcasting);
171  if (negative(setsockopt(socket, SOL_SOCKET, SO_BROADCAST, OPTYPE &arg,
172  sizeof(arg)))) {
173  LOG(a_sprintf("Could not change broadcast mode on %u.", socket));
174  return false;
175  }
176  return true;
177 }
178 
180 {
181  FUNCDEF("set_reuse_address");
182  int arg = int(reuse);
183  if (negative(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, OPTYPE &arg,
184  sizeof(arg)))) {
185  LOG(a_sprintf("Could not set reuse address mode on %u.", socket));
186  return false;
187  }
188  return true;
189 }
190 
191 bool raw_socket::set_keep_alive(basis::un_int socket, bool keep_alive)
192 {
193  FUNCDEF("set_keep_alive");
194  int arg = int(keep_alive);
195  if (negative(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, OPTYPE &arg,
196  sizeof(arg)))) {
197  LOG(a_sprintf("Could not set keep alive mode on %u.", socket));
198  return false;
199  }
200  return true;
201 }
202 
203 int raw_socket::select(basis::un_int socket, int mode, int timeout) const
204 {
205  FUNCDEF("select [single]");
206  if (!socket) return SI_ERRONEOUS;
207  fd_set_wrapper read_list, write_list, exceps;
208  int ret = inner_select(socket, mode, timeout, read_list, write_list, exceps);
209  if (!ret) return 0; // nothing is happening.
210  if (ret == SI_ERRONEOUS) return SI_ERRONEOUS; // something bad happened.
211  // otherwise we should be at base-line status.
212  return analyze_select_result(socket, mode, read_list, write_list, exceps);
213 }
214 
215 int raw_socket::inner_select(basis::un_int socket, int mode, int timeout,
216  fd_set_wrapper &read_list, fd_set_wrapper &write_list,
217  fd_set_wrapper &exceptions) const
218 {
219  FUNCDEF("inner_select");
220  // setup the file descriptor sets for the select. we check readability,
221  // writability and exception status.
222  FD_ZERO(&read_list); FD_SET(socket, &read_list);
223  FD_ZERO(&write_list); FD_SET(socket, &write_list);
224  FD_ZERO(&exceptions); FD_SET(socket, &exceptions);
225 
226  timeval base_time_out;
227  time_stamp::fill_timeval_ms(base_time_out, timeout);
228  // timeval has tv_sec=seconds, tv_usec=microseconds.
229 //#if !defined(__GNU_WINDOWS__)
230  timeval *time_out = &base_time_out;
231  /*
232 #elif defined(__GNU_WINDOWS__)
233  __ms_timeval win_time_out;
234  win_time_out.tv_sec = base_time_out.tv_sec;
235  win_time_out.tv_usec = base_time_out.tv_usec;
236  __ms_timeval *time_out = &win_time_out;
237 #endif
238 */
239 
240  // select will tell us about the socket.
241  int ret = ::select(socket + 1,
242  (mode & SELECTING_JUST_WRITE)? NULL_POINTER : &read_list,
243  (mode & SELECTING_JUST_READ)? NULL_POINTER : &write_list,
244  &exceptions, time_out);
245  int error = critical_events::system_error();
246  if (!ret) return 0; // nothing to report.
247 
248  if (ret == SOCKET_ERROR) {
249  switch (error) {
250  // all real errors fall out to the error handling stuff.
251  case SOCK_EFAULT: // intentional fall-through.
252  case SOCK_ENETDOWN: // intentional fall-through.
253  case SOCK_EINVAL: // intentional fall-through.
254  case SOCK_EINTR: // intentional fall-through.
255 /* #ifdef __WIN32__
256  case SOCK_NOTINITIALISED: // intentional fall-through.
257 #endif */
258  case SOCK_ENOTSOCK:
259  break;
260 
261  // hopefully all these others are bogus errors...
262  case SOCK_EINPROGRESS: // intentional fall-through.
263  case 0: // intentional fall-through.
264  default:
265 #ifdef DEBUG_RAW_SOCKET
266  LOG("got to weird case, in progress or zero.");
267 #endif
268  return 0; // not really an error.
269  }
270 #ifdef DEBUG_RAW_SOCKET
271  LOG(a_sprintf("socket %u had error %d in select: %s.",
272  socket, error, _stack->tcpip_error_name(error).s()));
273 #endif
274  return SI_ERRONEOUS;
275  }
276 
277  // if we got to here, then there are some things to report...
278  return SI_BASELINE;
279 }
280 
281 int raw_socket::test_readability(basis::un_int socket) const
282 {
283  FUNCDEF("test_readability");
284  basis::un_int len;
285  if (negative(ioctl(socket, IOCTL_READ, &len))) {
286  LOG(astring(astring::SPRINTF, "socket %u had ioctl error: %s.",
287  socket, _stack->tcpip_error_name(critical_events::system_error()).s()));
288  return SI_ERRONEOUS;
289  } else {
290  if (positive(len)) return SI_READABLE;
291  else return SI_DISCONNECTED;
292  }
293 }
294 
296  fd_set_wrapper &read_list, fd_set_wrapper &write_list,
297  fd_set_wrapper &exceptions) const
298 {
299 #ifdef DEBUG_RAW_SOCKET
300  FUNCDEF("analyze_select_result");
301 #endif
302  int to_return = 0;
303 
304  // in case of an exception, we return an error.
305  if (FD_ISSET(socket, &exceptions)) {
306 #ifdef DEBUG_RAW_SOCKET
307  LOG(astring(astring::SPRINTF, "exception seen for socket %u!", socket));
308 #endif
309  }
310 
311  // check to see if there are bytes to read.
312  if ( ! (mode & SELECTING_JUST_WRITE) && FD_ISSET(socket, &read_list)) {
313  // make sure we have data. if no data is available, it means a
314  // disconnect occurred.
315 
316  int readable = test_readability(socket);
317  if (readable == SI_ERRONEOUS)
318  to_return |= SI_ERRONEOUS;
319  else if (readable == SI_READABLE)
320  to_return |= SI_READABLE;
321  else if (readable == SI_DISCONNECTED) {
322  // we need to check multiple times to be sure the OS really means this.
323  // either windoze seems to report an erroneous disconnect every few
324  // days or there's a bad synchronization issue as yet uncovered.
325  bool really_disconnected = true;
326  for (int i = 0; i < MULTIPLE_DISCONNECT_CHECKS; i++) {
327  fd_set_wrapper read_list, write_list, exceps;
328  int temp_ret = inner_select(socket, SELECTING_JUST_READ, 0, read_list,
329  write_list, exceps);
330  // check the return value first...
331  if (!temp_ret) {
332  // nothing happening (a zero return) means the socket's no longer
333  // claiming to have a readable state; our disconnect condition is
334  // thus violated and we can leave.
335  really_disconnected = false;
336  break;
337  }
338  if (temp_ret == SI_ERRONEOUS) {
339  // this, on the other hand, sounds really bad. the socket doesn't
340  // seem to exist any more or something else horrid happened.
341  really_disconnected = true;
342  break;
343  }
344  // if the select worked, we can check the fd_set now for readability.
345  if (!FD_ISSET(socket, &read_list)) {
346  // we are not in a disconnected state without being told we're
347  // readable. supposedly.
348  really_disconnected = false;
349  break;
350  }
351  // now we really test the socket for readability by making sure there
352  // really is data pending on the socket. if it's readable but there's
353  // no data, then either a disconnection has occurred or is in progress.
354  readable = test_readability(socket);
355  if (readable != SI_DISCONNECTED) {
356  // we are not disconnected if there's really data waiting.
357  really_disconnected = false;
358  break;
359  }
360  }
361  if (really_disconnected) {
362 #ifdef DEBUG_RAW_SOCKET
363  LOG(a_sprintf("connection closed on socket %u.", socket));
364 #endif
365  to_return |= SI_DISCONNECTED;
366  }
367  }
368  }
369 
370  // check writability state.
371  if (! (mode & SELECTING_JUST_READ) && FD_ISSET(socket, &write_list)) {
372  to_return |= SI_WRITABLE;
373  }
374 
375  return to_return;
376 }
377 
378 int raw_socket::select(int_array &read_sox, int_array &write_sox,
379  int timeout) const
380 {
381 #ifdef DEBUG_RAW_SOCKET
382  FUNCDEF("select [multiple]");
383 #endif
384  if (!read_sox.length() && !write_sox.length())
385  return 0; // nothing happened to nothing.
386 
387  int to_return = 0; // will get bits slammed into it to report results.
388 
389  // setup the file descriptor sets for the select. we check readability,
390  // writability and exception status.
391  fd_set_wrapper read_list; FD_ZERO(&read_list);
392  fd_set_wrapper write_list; FD_ZERO(&write_list);
393  fd_set_wrapper exceptions; FD_ZERO(&exceptions);
394  // set up the lists with the sets we were handed.
395  basis::un_int highest = 0;
396  int i = 0;
397  for (i = 0; i < read_sox.length(); i++) {
398  basis::un_int sock = (basis::un_int)read_sox[i];
399  if (sock > highest) highest = sock;
400  FD_SET(sock, &read_list);
401  }
402  for (i = 0; i < write_sox.length(); i++) {
403  basis::un_int sock = (basis::un_int)write_sox[i];
404  if (sock > highest) highest = sock;
405  FD_SET(sock, &write_list);
406  }
407 
408  timeval base_time_out;
409  time_stamp::fill_timeval_ms(base_time_out, timeout);
410  // timeval has tv_sec=seconds, tv_usec=microseconds.
411 //#if !defined(__GNU_WINDOWS__)
412  timeval *time_out = &base_time_out;
413 /*#elif defined(__GNU_WINDOWS__)
414  __ms_timeval win_time_out;
415  win_time_out.tv_sec = base_time_out.tv_sec;
416  win_time_out.tv_usec = base_time_out.tv_usec;
417  __ms_timeval *time_out = &win_time_out;
418 #endif
419 */
420 
421  // select will tell us about the socket.
422  int ret = ::select(highest + 1,
423  (read_sox.length())? &read_list : NULL_POINTER,
424  (write_sox.length())? &write_list : NULL_POINTER,
425  &exceptions, time_out);
426  int error = critical_events::system_error();
427 
428  if (ret == SOCKET_ERROR) {
429  switch (error) {
430  // all real errors fall out to the error handling stuff.
431  case SOCK_EFAULT: // intentional fall-through.
432  case SOCK_ENETDOWN: // intentional fall-through.
433  case SOCK_EINVAL: // intentional fall-through.
434  case SOCK_EINTR: // intentional fall-through.
435 /*#ifdef __WIN32__
436  case SOCK_NOTINITIALISED: // intentional fall-through.
437 #endif*/
438  case SOCK_ENOTSOCK:
439  break;
440 
441  // hopefully all these others are bogus errors...
442  case SOCK_EINPROGRESS: // intentional fall-through.
443  case 0: // intentional fall-through.
444  default:
445 #ifdef DEBUG_RAW_SOCKET
446  LOG("got to weird case, in progress or zero.");
447 #endif
448 
449 //hmmm: fix retd sox? what's this outcome mean for the list?
450 
451  return 0; // not really an error.
452  }
453 #ifdef DEBUG_RAW_SOCKET
454  LOG(a_sprintf("sockets had error %d in select: %s.",
455  error, _stack->tcpip_error_name(error).s()));
456 #endif
457 
458 //hmmm: fix retd sox? what's this outcome mean for the list?
459 
460  return SI_ERRONEOUS;
461  } else if (!ret) {
462  // we know of nothing exciting for any of these.
463  read_sox.reset();
464  write_sox.reset();
465  return 0; // nothing to report.
466  }
467 
468  // if we got to here, then there are some things to report...
469  // iterate through the lists and check results.
470  for (int k = 0; k < read_sox.length(); k++) {
471  basis::un_int socket = read_sox[k];
472  int interim = analyze_select_result(socket, SELECTING_JUST_READ, read_list,
473  write_list, exceptions);
474  if (!interim) {
475  // nothing happened on that guy.
476  read_sox.zap(k, k);
477  k--; // skip back to before the whack.
478  continue;
479  }
480  to_return |= interim; // join the results with overall result.
481  }
482  for (int p = 0; p < write_sox.length(); p++) {
483  basis::un_int socket = write_sox[p];
484  int interim = analyze_select_result(socket, SELECTING_JUST_WRITE, read_list,
485  write_list, exceptions);
486  if (!interim) {
487  // nothing happened on that guy.
488  write_sox.zap(p, p);
489  p--; // skip back to before the whack.
490  continue;
491  }
492  to_return |= interim; // join the results with overall result.
493  }
494 
495  return to_return;
496 }
497 
498 } //namespace.
499 
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
void reset(int number=0, const contents *initial_contents=NULL_POINTER)
Resizes this array and sets the contents from an array of contents.
Definition: array.h:349
int length() const
Returns the current reported length of the allocated C array.
Definition: array.h:115
outcome zap(int start, int end)
Deletes from "this" the objects inclusively between "start" and "end".
Definition: array.h:769
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition: astring.h:97
A simple object that wraps a templated array of ints.
Definition: array.h:275
bool set_nagle_algorithm(basis::un_int socket, bool use_nagle=true)
Definition: raw_socket.cpp:155
int close(basis::un_int &socket)
Definition: raw_socket.cpp:89
int select(basis::un_int socket, int selection_mode, int timeout=0) const
Definition: raw_socket.cpp:203
int analyze_select_result(basis::un_int socket, int mode, fd_set_wrapper &read_list, fd_set_wrapper &write_list, fd_set_wrapper &exceptions) const
Definition: raw_socket.cpp:295
bool set_reuse_address(basis::un_int socket, bool reuse=true)
Definition: raw_socket.cpp:179
int ioctl(basis::un_int socket, int request, void *argp) const
Definition: raw_socket.cpp:123
bool set_keep_alive(basis::un_int socket, bool keep_alive=true)
Definition: raw_socket.cpp:191
static basis::astring interest_name(int to_name)
Definition: raw_socket.cpp:111
bool set_broadcast(basis::un_int socket, bool broadcasting=true)
Definition: raw_socket.cpp:167
bool set_non_blocking(basis::un_int socket, bool non_blocking=true)
Definition: raw_socket.cpp:139
Helpful functions for interacting with TCP/IP stacks.
Definition: tcpip_stack.h:38
static basis::astring tcpip_error_name(int error_value)
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
bool positive(const type &a)
positive returns true if "a" is greater than zero, or false otherwise.
Definition: functions.h:39
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
unsigned int un_int
Abbreviated name for unsigned integers.
Definition: definitions.h:62
bool negative(const type &a)
negative returns true if "a" is less than zero.
Definition: functions.h:43
A logger that sends to the console screen using the standard output device.
Provides access to the operating system's socket methods.
Definition: base_address.h:26
@ SI_CONNECTED
Definition: raw_socket.h:45
@ SI_DISCONNECTED
Definition: raw_socket.h:46
@ SI_ERRONEOUS
Definition: raw_socket.h:47
@ SI_BASELINE
Definition: raw_socket.h:48
@ SI_READABLE
Definition: raw_socket.h:43
@ SI_WRITABLE
Definition: raw_socket.h:44
const basis::un_int IOCTL_READ
Definition: raw_socket.cpp:62
const int MULTIPLE_DISCONNECT_CHECKS
Definition: raw_socket.cpp:52
void combine(astring &existing, const astring &addition)
Definition: raw_socket.cpp:103
const basis::un_int NON_BLOCKING
Definition: raw_socket.cpp:61
#include <time.h>
Definition: earth_time.cpp:37
#define OPTYPE
Definition: raw_socket.cpp:34
#define LOG(to_print)
Definition: raw_socket.cpp:50
#define SOCK_EINVAL
#define SOCKET_ERROR
#define SOCK_ENETDOWN
#define SOCK_EINPROGRESS
#define SOCK_EFAULT
#define SOCK_ENOTSOCK
#define SOCK_EINTR