1 /*****************************************************************************\
4 * Author : Chris Koeritz *
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 \*****************************************************************************/
15 #include "raw_socket.h"
16 #include "tcpip_stack.h"
18 #include <basis/functions.h>
19 #include <loggers/critical_events.h>
20 #include <loggers/program_wide_logger.h>
21 #include <timely/time_stamp.h>
28 #include <arpa/inet.h>
30 #include <netinet/tcp.h>
31 #include <sys/ioctl.h>
32 #include <sys/socket.h>
34 #define OPTYPE (void *)
37 #define OPTYPE (char *)
40 using namespace basis;
41 using namespace loggers;
42 using namespace timely;
46 //#define DEBUG_RAW_SOCKET
47 // uncomment for noisy diagnostics.
50 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
52 const int MULTIPLE_DISCONNECT_CHECKS = 28;
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.
57 class fd_set_wrapper : public fd_set {};
61 const basis::un_int NON_BLOCKING = FIONBIO;
62 const basis::un_int IOCTL_READ = FIONREAD;
66 // defined by winsock header but not present in the winsock dll.
67 int PASCAL FAR __WSAFDIsSet(SOCKET fd, fd_set FAR *the_set)
69 int i = the_set->fd_count;
71 if (the_set->fd_array[i] == fd)
80 raw_socket::raw_socket()
81 : _stack(new tcpip_stack())
84 raw_socket::~raw_socket()
89 int raw_socket::close(basis::un_int &socket)
93 to_return = closesocket(socket);
96 to_return = ::close(socket);
102 //move this into parser bits as a OR combiner or something.
103 void combine(astring &existing, const astring &addition)
106 if (existing.t()) existing += " | ";
107 existing += addition;
111 astring raw_socket::interest_name(int interest)
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");
123 int raw_socket::ioctl(basis::un_int socket, int request, void *argp) const
126 return ::ioctl(socket, request, argp);
130 return ioctlsocket(socket, request, (un_long *)argp);
132 return ioctlsocket(socket, request, (un_int *)argp);
137 bool raw_socket::set_non_blocking(basis::un_int socket, bool non_blocking)
139 FUNCDEF("set_non_blocking");
141 int curr_flags = fcntl(socket, F_GETFL, 0);
142 if (fcntl(socket, F_SETFL, curr_flags | O_NONBLOCK) < 0) return false;
144 int arg = int(non_blocking);
145 if (negative(ioctl(socket, NON_BLOCKING, &arg))) {
146 LOG(a_sprintf("Could not set non-blocking (FIONBIO) option on raw_socket %u.", socket));
153 bool raw_socket::set_nagle_algorithm(basis::un_int socket, bool use_nagle)
155 FUNCDEF("set_nagle_algorithm");
156 int arg = int(!use_nagle); // opposite of the flag, since we set no-delay.
157 if (negative(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, OPTYPE &arg,
159 LOG(a_sprintf("Could not change nagle coalescing mode on %u.", socket));
165 bool raw_socket::set_broadcast(basis::un_int socket, bool broadcasting)
167 FUNCDEF("set_broadcast");
168 int arg = int(broadcasting);
169 if (negative(setsockopt(socket, SOL_SOCKET, SO_BROADCAST, OPTYPE &arg,
171 LOG(a_sprintf("Could not change broadcast mode on %u.", socket));
177 bool raw_socket::set_reuse_address(basis::un_int socket, bool reuse)
179 FUNCDEF("set_reuse_address");
180 int arg = int(reuse);
181 if (negative(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, OPTYPE &arg,
183 LOG(a_sprintf("Could not set reuse address mode on %u.", socket));
189 bool raw_socket::set_keep_alive(basis::un_int socket, bool keep_alive)
191 FUNCDEF("set_keep_alive");
192 int arg = int(keep_alive);
193 if (negative(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, OPTYPE &arg,
195 LOG(a_sprintf("Could not set keep alive mode on %u.", socket));
201 int raw_socket::select(basis::un_int socket, int mode, int timeout) const
203 FUNCDEF("select [single]");
204 if (!socket) return SI_ERRONEOUS;
205 fd_set_wrapper read_list, write_list, exceps;
206 int ret = inner_select(socket, mode, timeout, read_list, write_list, exceps);
207 if (!ret) return 0; // nothing is happening.
208 if (ret == SI_ERRONEOUS) return SI_ERRONEOUS; // something bad happened.
209 // otherwise we should be at base-line status.
210 return analyze_select_result(socket, mode, read_list, write_list, exceps);
213 int raw_socket::inner_select(basis::un_int socket, int mode, int timeout,
214 fd_set_wrapper &read_list, fd_set_wrapper &write_list,
215 fd_set_wrapper &exceptions) const
217 FUNCDEF("inner_select");
218 // setup the file descriptor sets for the select. we check readability,
219 // writability and exception status.
220 FD_ZERO(&read_list); FD_SET(socket, &read_list);
221 FD_ZERO(&write_list); FD_SET(socket, &write_list);
222 FD_ZERO(&exceptions); FD_SET(socket, &exceptions);
224 timeval base_time_out;
225 time_stamp::fill_timeval_ms(base_time_out, timeout);
226 // timeval has tv_sec=seconds, tv_usec=microseconds.
227 #if !defined(__GNU_WINDOWS__)
228 timeval *time_out = &base_time_out;
229 #elif defined(__GNU_WINDOWS__)
230 __ms_timeval win_time_out;
231 win_time_out.tv_sec = base_time_out.tv_sec;
232 win_time_out.tv_usec = base_time_out.tv_usec;
233 __ms_timeval *time_out = &win_time_out;
236 // select will tell us about the socket.
237 int ret = ::select(socket + 1,
238 (mode & SELECTING_JUST_WRITE)? NULL_POINTER : &read_list,
239 (mode & SELECTING_JUST_READ)? NULL_POINTER : &write_list,
240 &exceptions, time_out);
241 int error = critical_events::system_error();
242 if (!ret) return 0; // nothing to report.
244 if (ret == SOCKET_ERROR) {
246 // all real errors fall out to the error handling stuff.
247 case SOCK_EFAULT: // intentional fall-through.
248 case SOCK_ENETDOWN: // intentional fall-through.
249 case SOCK_EINVAL: // intentional fall-through.
250 case SOCK_EINTR: // intentional fall-through.
252 case SOCK_NOTINITIALISED: // intentional fall-through.
257 // hopefully all these others are bogus errors...
258 case SOCK_EINPROGRESS: // intentional fall-through.
259 case 0: // intentional fall-through.
261 #ifdef DEBUG_RAW_SOCKET
262 LOG("got to weird case, in progress or zero.");
264 return 0; // not really an error.
266 #ifdef DEBUG_RAW_SOCKET
267 LOG(a_sprintf("socket %u had error %d in select: %s.",
268 socket, error, _stack->tcpip_error_name(error).s()));
273 // if we got to here, then there are some things to report...
277 int raw_socket::test_readability(basis::un_int socket) const
279 FUNCDEF("test_readability");
281 if (negative(ioctl(socket, IOCTL_READ, &len))) {
282 LOG(astring(astring::SPRINTF, "socket %u had ioctl error: %s.",
283 socket, _stack->tcpip_error_name(critical_events::system_error()).s()));
286 if (positive(len)) return SI_READABLE;
287 else return SI_DISCONNECTED;
291 int raw_socket::analyze_select_result(basis::un_int socket, int mode,
292 fd_set_wrapper &read_list, fd_set_wrapper &write_list,
293 fd_set_wrapper &exceptions) const
295 #ifdef DEBUG_RAW_SOCKET
296 FUNCDEF("analyze_select_result");
300 // in case of an exception, we return an error.
301 if (FD_ISSET(socket, &exceptions)) {
302 #ifdef DEBUG_RAW_SOCKET
303 LOG(astring(astring::SPRINTF, "exception seen for socket %u!", socket));
307 // check to see if there are bytes to read.
308 if ( ! (mode & SELECTING_JUST_WRITE) && FD_ISSET(socket, &read_list)) {
309 // make sure we have data. if no data is available, it means a
310 // disconnect occurred.
312 int readable = test_readability(socket);
313 if (readable == SI_ERRONEOUS)
314 to_return |= SI_ERRONEOUS;
315 else if (readable == SI_READABLE)
316 to_return |= SI_READABLE;
317 else if (readable == SI_DISCONNECTED) {
318 // we need to check multiple times to be sure the OS really means this.
319 // either windoze seems to report an erroneous disconnect every few
320 // days or there's a bad synchronization issue as yet uncovered.
321 bool really_disconnected = true;
322 for (int i = 0; i < MULTIPLE_DISCONNECT_CHECKS; i++) {
323 fd_set_wrapper read_list, write_list, exceps;
324 int temp_ret = inner_select(socket, SELECTING_JUST_READ, 0, read_list,
326 // check the return value first...
328 // nothing happening (a zero return) means the socket's no longer
329 // claiming to have a readable state; our disconnect condition is
330 // thus violated and we can leave.
331 really_disconnected = false;
334 if (temp_ret == SI_ERRONEOUS) {
335 // this, on the other hand, sounds really bad. the socket doesn't
336 // seem to exist any more or something else horrid happened.
337 really_disconnected = true;
340 // if the select worked, we can check the fd_set now for readability.
341 if (!FD_ISSET(socket, &read_list)) {
342 // we are not in a disconnected state without being told we're
343 // readable. supposedly.
344 really_disconnected = false;
347 // now we really test the socket for readability by making sure there
348 // really is data pending on the socket. if it's readable but there's
349 // no data, then either a disconnection has occurred or is in progress.
350 readable = test_readability(socket);
351 if (readable != SI_DISCONNECTED) {
352 // we are not disconnected if there's really data waiting.
353 really_disconnected = false;
357 if (really_disconnected) {
358 #ifdef DEBUG_RAW_SOCKET
359 LOG(a_sprintf("connection closed on socket %u.", socket));
361 to_return |= SI_DISCONNECTED;
366 // check writability state.
367 if (! (mode & SELECTING_JUST_READ) && FD_ISSET(socket, &write_list)) {
368 to_return |= SI_WRITABLE;
374 int raw_socket::select(int_array &read_sox, int_array &write_sox,
377 #ifdef DEBUG_RAW_SOCKET
378 FUNCDEF("select [multiple]");
380 if (!read_sox.length() && !write_sox.length())
381 return 0; // nothing happened to nothing.
383 int to_return = 0; // will get bits slammed into it to report results.
385 // setup the file descriptor sets for the select. we check readability,
386 // writability and exception status.
387 fd_set_wrapper read_list; FD_ZERO(&read_list);
388 fd_set_wrapper write_list; FD_ZERO(&write_list);
389 fd_set_wrapper exceptions; FD_ZERO(&exceptions);
390 // set up the lists with the sets we were handed.
391 basis::un_int highest = 0;
393 for (i = 0; i < read_sox.length(); i++) {
394 basis::un_int sock = (basis::un_int)read_sox[i];
395 if (sock > highest) highest = sock;
396 FD_SET(sock, &read_list);
398 for (i = 0; i < write_sox.length(); i++) {
399 basis::un_int sock = (basis::un_int)write_sox[i];
400 if (sock > highest) highest = sock;
401 FD_SET(sock, &write_list);
404 timeval base_time_out;
405 time_stamp::fill_timeval_ms(base_time_out, timeout);
406 // timeval has tv_sec=seconds, tv_usec=microseconds.
407 #if !defined(__GNU_WINDOWS__)
408 timeval *time_out = &base_time_out;
409 #elif defined(__GNU_WINDOWS__)
410 __ms_timeval win_time_out;
411 win_time_out.tv_sec = base_time_out.tv_sec;
412 win_time_out.tv_usec = base_time_out.tv_usec;
413 __ms_timeval *time_out = &win_time_out;
416 // select will tell us about the socket.
417 int ret = ::select(highest + 1,
418 (read_sox.length())? &read_list : NULL_POINTER,
419 (write_sox.length())? &write_list : NULL_POINTER,
420 &exceptions, time_out);
421 int error = critical_events::system_error();
423 if (ret == SOCKET_ERROR) {
425 // all real errors fall out to the error handling stuff.
426 case SOCK_EFAULT: // intentional fall-through.
427 case SOCK_ENETDOWN: // intentional fall-through.
428 case SOCK_EINVAL: // intentional fall-through.
429 case SOCK_EINTR: // intentional fall-through.
431 case SOCK_NOTINITIALISED: // intentional fall-through.
436 // hopefully all these others are bogus errors...
437 case SOCK_EINPROGRESS: // intentional fall-through.
438 case 0: // intentional fall-through.
440 #ifdef DEBUG_RAW_SOCKET
441 LOG("got to weird case, in progress or zero.");
444 //hmmm: fix retd sox? what's this outcome mean for the list?
446 return 0; // not really an error.
448 #ifdef DEBUG_RAW_SOCKET
449 LOG(a_sprintf("sockets had error %d in select: %s.",
450 error, _stack->tcpip_error_name(error).s()));
453 //hmmm: fix retd sox? what's this outcome mean for the list?
457 // we know of nothing exciting for any of these.
460 return 0; // nothing to report.
463 // if we got to here, then there are some things to report...
464 // iterate through the lists and check results.
465 for (int k = 0; k < read_sox.length(); k++) {
466 basis::un_int socket = read_sox[k];
467 int interim = analyze_select_result(socket, SELECTING_JUST_READ, read_list,
468 write_list, exceptions);
470 // nothing happened on that guy.
472 k--; // skip back to before the whack.
475 to_return |= interim; // join the results with overall result.
477 for (int p = 0; p < write_sox.length(); p++) {
478 basis::un_int socket = write_sox[p];
479 int interim = analyze_select_result(socket, SELECTING_JUST_WRITE, read_list,
480 write_list, exceptions);
482 // nothing happened on that guy.
484 p--; // skip back to before the whack.
487 to_return |= interim; // join the results with overall result.