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
40using namespace basis;
41using namespace loggers;
42using namespace timely;
43
44namespace 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
57class fd_set_wrapper : public fd_set {};
58
60
62const basis::un_int IOCTL_READ = FIONREAD;
63
64/*
65#ifdef __WIN32__
66// defined by winsock header but not present in the winsock dll.
67int 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
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.
103void 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
123int 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
139bool 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
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
167bool 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
191bool 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
203int 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
215int 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
281int 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
378int 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
#define close
Definition Xos2defs.h:13
#define LOG(s)
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
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition astring.h:97
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition astring.h:113
A simple object that wraps a templated array of ints.
Definition array.h:275
static basis::un_int system_error()
gets the most recent system error reported on this thread.
bool set_nagle_algorithm(basis::un_int socket, bool use_nagle=true)
int close(basis::un_int &socket)
int select(basis::un_int socket, int selection_mode, int timeout=0) const
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
bool set_reuse_address(basis::un_int socket, bool reuse=true)
int ioctl(basis::un_int socket, int request, void *argp) const
bool set_keep_alive(basis::un_int socket, bool keep_alive=true)
static basis::astring interest_name(int to_name)
bool set_broadcast(basis::un_int socket, bool broadcasting=true)
bool set_non_blocking(basis::un_int socket, bool non_blocking=true)
Helpful functions for interacting with TCP/IP stacks.
Definition tcpip_stack.h:38
static basis::astring tcpip_error_name(int error_value)
static void fill_timeval_ms(timeval &time_point, int milliseconds)
returns a timeval system object that represents the "milliseconds".
#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:54
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.
@ 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
const int MULTIPLE_DISCONNECT_CHECKS
void combine(astring &existing, const astring &addition)
const basis::un_int NON_BLOCKING
#include <time.h>
#define OPTYPE
#define SOCK_EINVAL
#define SOCKET_ERROR
#define SOCK_ENETDOWN
#define SOCK_EINPROGRESS
#define SOCK_EFAULT
#define SOCK_ENOTSOCK
#define SOCK_EINTR