first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / octopi / library / sockets / spocket.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : spocket                                                           *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2001-$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 "internet_address.h"
16 #include "raw_socket.h"
17 #include "spocket.h"
18 #include "tcpip_stack.h"
19
20 #include <basis/byte_array.h>
21 #include <basis/functions.h>
22 #include <basis/astring.h>
23 #include <basis/mutex.h>
24 #include <loggers/critical_events.h>
25 #include <loggers/program_wide_logger.h>
26 #include <structures/static_memory_gremlin.h>
27 #include <timely/time_control.h>
28 #include <timely/time_stamp.h>
29
30 #ifdef __UNIX__
31   #include <arpa/inet.h>
32   #include <errno.h>
33   #include <netdb.h>
34   #include <signal.h>
35   #include <string.h>
36   #include <sys/ioctl.h>
37   #include <sys/socket.h>
38   #include <sys/types.h>
39   #include <termios.h>
40   #include <unistd.h>
41 #endif
42
43 using namespace basis;
44 using namespace loggers;
45 using namespace structures;
46 using namespace timely;
47
48 namespace sockets {
49
50 //#define DEBUG_SPOCKET
51   // uncomment for noisy version.
52
53 #undef LOG
54 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print)
55
56 const int PENDING_CONNECTIONS_ALLOWED = 14;
57   // we allow this many connections to queue up before they get rejected.
58   // if the OS is windoze, this number is ignored if it's greater than the
59   // hardcoded maximum of like 5.
60
61 const int RESOLVE_INTERVAL = 300;
62   // we'll re-resolve the ip address at this rate.  this mainly comes into
63   // play for the connect call, since the address passed in could have changed
64   // or been invalid to start with.  we're not losing much by trying to
65   // resolve the address again during connection time.
66
67 #define RECOGNIZE_DISCO \
68   _client_bind = false; \
69   _was_connected = false
70
71 // ensure that the socket is in a good state.
72 #define ENSURE_HEALTH(retval) \
73   if (!_was_connected) return retval;  /* never has been. */ \
74   if (!_socket) { RECOGNIZE_DISCO; return retval; /* not set. */ }
75
76 #define CHECK_BOGUS(retval) \
77   if (is_bogus()) { return retval;  /* this spocket is junk. */ }
78
79 #undef GRAB_LOCK
80 #ifdef __WIN32__
81   // win32 seems to trip over selects unless we protect them.
82   #define GRAB_LOCK auto_synchronizer l(*_select_lock)
83     // and in truth, the locking turns out to be needed on win32 if we're
84     // going to allow sharing a spocket across threads.  this is one of the
85     // design goals so we're honor bound to support that.
86 #else
87   #define GRAB_LOCK 
88 #endif
89
90 #ifdef __UNIX__
91   SAFE_STATIC(mutex, __broken_pipe_synch, )
92 #endif
93
94 spocket::spocket(const internet_address &where, sock_types type)
95 : _type(type),
96   _socket(0),
97   _server_socket(0),
98   _was_connected(false),
99   _client(false),
100   _where(new internet_address(where)),
101   _remote(new internet_address),
102   _socks(new raw_socket),
103   _stack(new tcpip_stack),
104   _select_lock(new mutex),
105   _last_resolve(new time_stamp),  // don't force an immediate resolve.
106   _client_bind(false),
107   _cli_bind(new internet_address)
108 {
109   FUNCDEF("constructor");
110   if ( (_type == BROADCAST) || (_type == UNICAST) ) {
111     // casting types are never servers.
112     _client = true;
113   } else if ( (type == CONNECTED) || (type == BOGUS_SOCK) ) {
114     // nothing special here currently.
115   } else {
116     // this is an unknown type.
117     LOG(a_sprintf("unknown socket type %d; failing out.", _type));
118 //hmmm: without a validity flag of some sort, this doesn't mean much.
119     return;
120   }
121 }
122
123 spocket::~spocket()
124 {
125 #ifdef DEBUG_SPOCKET
126   FUNCDEF("destructor");
127   LOG(a_sprintf("closing spocket: ") + text_form());
128 #endif
129   disconnect();
130   WHACK(_where);
131   WHACK(_socks);
132   WHACK(_stack);
133   WHACK(_remote);
134   WHACK(_select_lock);
135   WHACK(_last_resolve);
136   WHACK(_cli_bind);
137   _client_bind = false;
138 }
139
140 // where and remote don't need to be protected unless we revise the design of
141 // the class and allow a reset or re-open kind of method.
142 const internet_address &spocket::where() const { return *_where; }
143 const internet_address &spocket::remote() const { return *_remote; }
144
145 tcpip_stack &spocket::stack() const { return *_stack; }
146
147 // doesn't need to be protected since the sockets are being treated as simple
148 // ints and since _where currently does not get destroyed.
149 astring spocket::text_form()
150 {
151   a_sprintf sock_string("socket=%d", _socket);
152   if (is_root_server())
153     sock_string += a_sprintf("root-socket=%d", _server_socket);
154
155   return a_sprintf("%s spocket: %s, %s, %s",
156       (is_client()? "client" :
157          (is_root_server()? "root-server" : "server") ),
158       (connected()? "connected" :
159          (was_connected()? "unconnected (was once)" : "never-connected") ),
160       sock_string.s(),
161       _where->text_form().s());
162 }
163
164 void spocket::bind_client(const internet_address &addr)
165 {
166   _client_bind = true;
167   *_cli_bind = addr;
168 }
169
170 const char *spocket::outcome_name(const outcome &to_name)
171 {
172   switch (to_name.value()) {
173     case NOT_SERVER: return "NOT_SERVER";
174     default: return communication_commons::outcome_name(to_name);
175   }
176 }
177
178 outcome spocket::disconnect()
179 {
180 #ifdef DEBUG_SPOCKET
181   FUNCDEF("disconnect");
182 #endif
183   RECOGNIZE_DISCO; 
184   if (_socket) {
185 #ifdef DEBUG_SPOCKET
186     LOG(a_sprintf("closing socket %d", _socket));
187 #endif
188     _socks->close(_socket);
189     _socket = 0;
190   }
191   if (_server_socket) {
192 #ifdef DEBUG_SPOCKET
193     LOG(a_sprintf("closing server socket %d", _server_socket));
194 #endif
195     _socks->close(_server_socket);
196     _server_socket = 0;
197   }
198   return OKAY;
199 }
200
201 bool spocket::connected()
202 {
203 #ifdef DEBUG_SPOCKET
204   FUNCDEF("connected");
205 #endif
206   ENSURE_HEALTH(false);
207
208   if (_type != CONNECTED) return _was_connected;
209
210   // do examination on spocket.
211   int sel_mode = 0;
212   GRAB_LOCK;
213   int ret = _socks->select(_socket, sel_mode);
214
215   if (ret == 0) {
216     return true;  // we are happy.
217   }
218   if ( (ret & SI_DISCONNECTED) || (ret & SI_ERRONEOUS) ) {
219     RECOGNIZE_DISCO; 
220     return false;
221   }
222   return true;
223 }
224
225 outcome spocket::await_readable(int timeout)
226 {
227 #ifdef DEBUG_SPOCKET
228   FUNCDEF("await_readable");
229 #endif
230   CHECK_BOGUS(NO_CONNECTION);
231   ENSURE_HEALTH(NO_CONNECTION);
232   GRAB_LOCK;
233   int mode = raw_socket::SELECTING_JUST_READ;
234   int ret = _socks->select(_socket, mode, timeout);
235   if (ret & SI_READABLE) return OKAY;
236     // we found something to report.
237   if (ret & SI_DISCONNECTED) {
238     RECOGNIZE_DISCO; 
239     return NO_CONNECTION;
240   }
241   return _socket? NONE_READY : NO_CONNECTION;
242     // nothing is ready currently.
243 }
244
245 outcome spocket::await_writable(int timeout)
246 {
247 #ifdef DEBUG_SPOCKET
248   FUNCDEF("await_writable");
249 #endif
250   CHECK_BOGUS(NO_CONNECTION);
251   ENSURE_HEALTH(NO_CONNECTION);
252   GRAB_LOCK;
253   int mode = raw_socket::SELECTING_JUST_WRITE;
254   int ret = _socks->select(_socket, mode, timeout);
255   if (ret & SI_WRITABLE) return OKAY;
256     // we found something to report.
257   if (ret & SI_DISCONNECTED) {
258     RECOGNIZE_DISCO; 
259     return NO_CONNECTION;
260   }
261   return _socket? NONE_READY : NO_CONNECTION;
262     // nothing is ready currently.
263 }
264
265 outcome spocket::connect(int communication_wait)
266 {
267   FUNCDEF("connect");
268   CHECK_BOGUS(NO_CONNECTION);
269   {
270     GRAB_LOCK;  // short lock.
271     if ( (_was_connected && !_client) || _server_socket) {
272 #ifdef DEBUG_SPOCKET
273       LOG("this object was already opened as a server!");
274 #endif
275       return BAD_INPUT;
276     }
277     _client = true;  // set our state now that we're sure this is okay.
278     _was_connected = false;  // reset this, since we're connecting now.
279   }
280
281   if (!_socket) {
282     // the socket was never created (or was cleaned up previously).  this is
283     // where we create the socket so we can communicate.
284 #ifdef DEBUG_SPOCKET
285     LOG(astring("creating socket now for ") + _where->text_form());
286 #endif
287     GRAB_LOCK;
288     int sock_type = SOCK_STREAM;
289     int proto = IPPROTO_TCP;
290
291     if ( (_type == BROADCAST) || (_type == UNICAST) ) {
292       sock_type = SOCK_DGRAM;
293       proto = IPPROTO_IP;
294     }
295     _socket = int(::socket(AF_INET, sock_type, proto));
296     if ( (_socket == basis::un_int(INVALID_SOCKET)) || !_socket) {
297       _socket = NIL;
298       LOG("Failed to open the client's connecting spocket.");
299       return ACCESS_DENIED;
300     }
301
302     // mark the spocket for _blocking_ I/O.  we want connect to sit there
303     // until it's connected or returns with an error.
304     _socks->set_non_blocking(_socket, false);
305
306     if (_type == BROADCAST) {
307       if (!_socks->set_broadcast(_socket)) return ACCESS_DENIED;
308         // mark the socket for broadcast capability.
309     }
310
311     if (!_socks->set_reuse_address(_socket)) return ACCESS_DENIED;
312       // mark the socket so we don't get bind errors on in-use conditions.
313   }
314
315   if (_type == CONNECTED) {
316     GRAB_LOCK;
317     // turn on the keepalive timer so that loss of the connection will
318     // eventually be detected by the OS.  the duration that is allowed to
319     // elapse before a dead connection is noticed varies with the operating
320     // system and is not configured at this level.
321     if (!_socks->set_keep_alive(_socket)) {
322 #ifdef DEBUG_SPOCKET
323       LOG("couldn't set watchdog timer on socket.");
324 #endif
325     }
326
327 //hmmm: doesn't this need to be done for bcast too?
328
329     // create the spocket address that we will connect to.
330     if (strlen(_where->hostname)
331 //        && (_where->is_nil_address() 
332 //            || (*_last_resolve < time_stamp(-RESOLVE_INTERVAL) ) ) ) {
333 //
334 //moving to always re-resolving before a connect.  otherwise we have somewhat
335 //hard to predict behavior about when the re-resolve will happen.
336           ) {
337       // we know we need to resolve if the address is NIL or if the re-resolve
338       // interval has elapsed.
339       astring full_host;
340       byte_array ip_addr = _stack->full_resolve(_where->hostname, full_host);
341       if (ip_addr.length()) {
342         ip_addr.stuff(internet_address::ADDRESS_SIZE, _where->ip_address);
343         LOG(astring("successfully re-resolved address--") + _where->text_form());
344       }
345       *_last_resolve = time_stamp();  // reset since we just resolved.
346     }
347
348     // special code for forcing a client to bind.
349     if (_client_bind) {
350       sockaddr sock = _stack->convert(*_cli_bind);
351
352 #ifdef DEBUG_SPOCKET
353       LOG(a_sprintf("binding client socket %d to ", _socket)
354           + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
355 #endif
356
357       // now, the socket address is bound to our socket.
358       if (negative(bind(_socket, &sock, sizeof(sock)))) {
359         LOG(a_sprintf("error binding socket %d to ", _socket)
360             + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
361       }
362     }
363
364   } else if ( (_type == BROADCAST) || (_type == UNICAST) ) {
365     // this is the last piece of preparation for a broadcast or unicast socket.
366     // there's no real connection, so we just need to get it bound and ready
367     // to fling packets.
368     GRAB_LOCK;
369     sockaddr sock = _stack->convert(*_where);
370
371 #ifdef DEBUG_SPOCKET
372     LOG(a_sprintf("binding socket %d to ", _socket)
373         + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
374 #endif
375
376     // now, the socket address is bound to our socket.
377     if (negative(bind(_socket, &sock, sizeof(sock)))) {
378       LOG(a_sprintf("error binding socket %d to ", _socket)
379           + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
380     }
381
382     // that's it for broadcast preparation.  we should be ready.
383     _was_connected = true;
384     return OKAY;
385   }
386
387   // the following is for connected mode only.
388
389   sockaddr sock = _stack->convert(*_where);
390
391   // attempt the connection now.
392
393 //hmmm: error returns are done differently on bsd, right?
394 //hmmm: perhaps hide the base connect in a func that sets our internal
395 //      error variable and then allows comparison to enums we provide.
396
397   time_stamp abort_time(communication_wait);
398
399   bool connected = false;  // did we connect.
400
401   int sock_len = sizeof(sock);
402
403   while (time_stamp() < abort_time) {
404     // make the low-level socket connection.
405     int ret = ::connect(_socket, &sock, sock_len);
406     if (ret != SOCKET_ERROR) {
407       connected = true;
408       _socks->set_non_blocking(_socket, true);
409       break;
410     }
411
412     basis::un_int last_error = critical_events::system_error();
413
414     // if we're already done, then make this look like a normal connect.
415     if (last_error == SOCK_EISCONN) {
416       connected = true;
417       break;
418     }
419
420     if ( (last_error != SOCK_EWOULDBLOCK)
421         && (last_error != SOCK_EINPROGRESS) ) {
422       // this seems like a real error here.
423 #ifdef DEBUG_SPOCKET
424       LOG(a_sprintf("Connect failed (error %s or %d) on address:",
425           critical_events::system_error_text(last_error).s(), last_error)
426           + _where->text_form());
427 #endif
428       if (last_error == SOCK_ECONNREFUSED) return NO_ANSWER;
429 //hmmm: fix more of the possibilities to be sensible outcomes?
430       return ACCESS_DENIED;
431     }
432
433     if (time_stamp() >= abort_time) break;  // skip before sleeping if T.O.
434
435     // snooze for a bit before trying again.
436     time_control::sleep_ms(10);
437   }
438
439   if (connected) {
440 #ifdef DEBUG_SPOCKET
441     LOG(a_sprintf("socket %d connected to server.", _socket));
442 #endif
443     GRAB_LOCK;  // short lock.
444     _was_connected = true;
445     return OKAY;
446   }
447
448   return TIMED_OUT;
449 }
450
451 outcome spocket::accept(spocket * &sock, bool wait)
452 {
453   FUNCDEF("accept");
454   CHECK_BOGUS(NO_CONNECTION);
455   if (_type != CONNECTED) return BAD_INPUT;
456
457   // we don't lock in here; we should not be locking on the server socket.
458
459   sock = NIL;  // reset.
460
461   if (_socket) {
462 #ifdef DEBUG_SPOCKET
463     LOG("tried to accept on a client spocket.");
464 #endif
465     return NOT_SERVER;
466   }
467   _client = false;
468
469   if (!_server_socket) {
470     _server_socket = int(::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
471 #ifdef DEBUG_SPOCKET
472     LOG(a_sprintf("srv sock is %d", _server_socket));
473 #endif
474
475 #ifdef DEBUG_SPOCKET
476     LOG(astring("creating server socket now for ") + _where->text_form());
477 #endif
478
479     if (_server_socket == basis::un_int(INVALID_SOCKET)) {
480       LOG("Failed to open the serving spocket.");
481       return BAD_INPUT;
482     }
483
484     // mark the socket so we don't get bind errors on in-use conditions.
485     if (!_socks->set_reuse_address(_server_socket)) 
486       LOG("Failed to mark the socket for re-use.");
487
488     // create the spocket address for where we exist.
489     sockaddr sock = _stack->convert(*_where);
490
491     // now, the spocket address is bound to our spocket.
492     int sock_len = sizeof(sock);
493     if (bind(_server_socket, (sockaddr *)&sock, sock_len) < 0) {
494       LOG(astring("Error on bind of ") + critical_events::system_error_text(critical_events::system_error()));
495       _socks->close(_server_socket);
496       return ACCESS_DENIED;
497     }
498
499     // now listen for a connection on our spocket.
500     if (listen(_server_socket, PENDING_CONNECTIONS_ALLOWED) < 0) {
501       LOG(astring("Listen failed with error of ")
502           + critical_events::system_error_text(critical_events::system_error()));
503       _socks->close(_server_socket);
504       return ACCESS_DENIED;
505     }
506   }
507
508   // do the kind of accept they want; either block on it or don't.
509   // since our server socket is never used for sends or receives, we pretty
510   // much control it completely and this is safe.
511   if (!wait) {
512     _socks->set_non_blocking(_server_socket, true);
513       // mark our socket as non-blocking so we don't get stuck in accepts.
514   } else {
515     _socks->set_non_blocking(_server_socket, false);
516       // mark our socket as blocking; we will be paused until accept occurs.
517   }
518
519   // now try accepting a connection on the spocket.
520   sockaddr new_sock;
521   socklen_t sock_len = sizeof(new_sock);
522   int accepted = int(::accept(_server_socket, &new_sock, &sock_len));
523   int error = critical_events::system_error();
524   if (accepted == INVALID_SOCKET) {
525     if (error == SOCK_EWOULDBLOCK) return NO_CONNECTION;
526 #ifdef DEBUG_SPOCKET
527     LOG(astring("Accept got no client, with an error of ")
528         + critical_events::system_error_text(error));
529 #endif
530     return ACCESS_DENIED;
531   }
532
533   // mark the new spocket for non-blocking I/O.
534   _socks->set_non_blocking(accepted, true);
535
536 //move to socks object!
537   int sock_hop = 1;
538   if (setsockopt(accepted, SOL_SOCKET, SO_KEEPALIVE, (char *)&sock_hop,
539       sizeof(sock_hop)) < 0) {
540 #ifdef DEBUG_SPOCKET
541     LOG("couldn't set watchdog timer on socket.");
542 #endif
543   }
544
545   // create the spocket address that we will connect to.
546 #ifdef DEBUG_SPOCKET
547   LOG(astring("accepted a client socket for ") + _where->text_form());
548 #endif
549
550 // NOTE: normally, our network code sets the spocket to be kept alive (using
551 //       keep alives), but we are trying to have a minimal spocket usage and
552 //       a minimal network load for this test scenario.
553
554   sock = new spocket(*_where);
555   *sock->_remote = _stack->convert(new_sock);
556   sock->_socket = accepted;
557   sock->_server_socket = 0;  // reset to avoid whacking.
558   sock->_was_connected = true;
559   return OKAY;
560 }
561
562 outcome spocket::send(const byte_array &to_send, int &len_sent)
563 {
564   return send(to_send.observe(), to_send.length(), len_sent);
565 }
566
567 outcome spocket::send(const abyte *buffer, int size, int &len_sent)
568 {
569 #ifdef DEBUG_SPOCKET
570   FUNCDEF("send");
571 #endif
572   CHECK_BOGUS(OKAY);
573   if (_type != CONNECTED) return BAD_INPUT;
574   GRAB_LOCK;
575   ENSURE_HEALTH(NO_CONNECTION);
576
577   len_sent = ::send(_socket, (char *)buffer, size, 0);
578   int error_code = critical_events::system_error();
579   if (!len_sent) {
580 #ifdef DEBUG_SPOCKET
581     LOG("No data went out on the spocket.");
582 #endif
583     return PARTIAL;
584   }
585   if (len_sent == SOCKET_ERROR) {
586     if (error_code == SOCK_EWOULDBLOCK) {
587 #ifdef DEBUG_SPOCKET
588       LOG("would block, will try later...");
589       if (len_sent > 0)
590         LOG("HEY HEY!  some was sent but we were not counting it!!!");
591 #endif
592       return NONE_READY;
593     }
594 #ifdef DEBUG_SPOCKET
595     LOG(astring("Error ") + critical_events::system_error_text(error_code)
596         + " occurred during the send!");
597 #endif
598     if (!connected()) return NO_CONNECTION;
599 #ifdef DEBUG_SPOCKET
600     LOG(a_sprintf("forcing disconnect on socket %d.", _socket));
601 #endif
602     // we're trying this new approach here...  we found that the socket doesn't
603     // really know that it got disconnected in some circumstances.
604     disconnect();
605     return ACCESS_DENIED;
606   }
607   if (len_sent != size) {
608     // only sent part of the buffer.
609 #ifdef DEBUG_SPOCKET
610     LOG(a_sprintf("sent %d bytes out of %d.", len_sent, size));
611 #endif
612     return PARTIAL;
613   }
614
615   return OKAY;
616 }
617
618 outcome spocket::send_to(const internet_address &where_to,
619     const byte_array &to_send, int &len_sent)
620 {
621   return send_to(where_to, to_send.observe(), to_send.length(), len_sent);
622 }
623
624 outcome spocket::send_to(const internet_address &where_to, const abyte *to_send,
625     int size, int &len_sent)
626 {
627   FUNCDEF("send_to");
628   CHECK_BOGUS(OKAY);
629   if (_type == CONNECTED) return BAD_INPUT;
630   sockaddr dest = _stack->convert(where_to);
631   int ret = sendto(_socket, (char *)to_send, size, 0, &dest, sizeof(dest));
632   int error = critical_events::system_error();
633   if (ret < 0) {
634     if (error == SOCK_EWOULDBLOCK) return NONE_READY;  // no buffer space?
635     LOG(astring("failed to send packet; error ")
636         + _stack->tcpip_error_name(error));
637     return ACCESS_DENIED;
638   }
639   if (ret != size) {
640     LOG(astring("didn't send whole datagram!"));
641   }
642   len_sent = ret;
643   return OKAY;
644 }
645
646 outcome spocket::receive(byte_array &buffer, int &size)
647 {
648 #ifdef DEBUG_SPOCKET
649   FUNCDEF("receive");
650 #endif
651   CHECK_BOGUS(NONE_READY);
652   if (_type != CONNECTED) return BAD_INPUT;
653   if (size <= 0) return BAD_INPUT;
654   buffer.reset(size);
655   outcome to_return = receive(buffer.access(), size);
656   // trim the buffer to the actual received size.
657   if (to_return == OKAY)
658     buffer.zap(size, buffer.last());
659   return to_return;
660 }
661
662 outcome spocket::receive(abyte *buffer, int &size)
663 {
664 #ifdef DEBUG_SPOCKET
665   FUNCDEF("receive");
666 #endif
667   CHECK_BOGUS(NONE_READY);
668   if (_type != CONNECTED) return BAD_INPUT;
669   ENSURE_HEALTH(NO_CONNECTION);
670   int expected = size;
671   size = 0;
672   if (expected <= 0) return BAD_INPUT;
673   GRAB_LOCK;
674   int len = recv(_socket, (char *)buffer, expected, 0);
675   if (!len) {
676     // check to make sure we're not disconnected.
677     int ret = _socks->select(_socket, raw_socket::SELECTING_JUST_READ);
678     if (ret & SI_DISCONNECTED) {
679       RECOGNIZE_DISCO; 
680       return NO_CONNECTION;
681     }
682     // seems like more normal absence of data.
683     return NONE_READY;
684   } else if (len < 0) {
685     if (critical_events::system_error() == SOCK_EWOULDBLOCK) return NONE_READY;
686 #ifdef DEBUG_SPOCKET
687     LOG(astring("The receive failed with an error ")
688         + critical_events::system_error_text(critical_events::system_error()));
689 #endif
690     if (!connected()) return NO_CONNECTION;
691     return ACCESS_DENIED;
692   }
693   size = len;
694   return OKAY;
695 }
696
697 outcome spocket::receive_from(byte_array &buffer, int &size,
698     internet_address &where_from)
699 {
700 #ifdef DEBUG_SPOCKET
701   FUNCDEF("receive_from");
702 #endif
703   where_from = internet_address();
704   CHECK_BOGUS(NONE_READY);
705   if (_type == CONNECTED) return BAD_INPUT;
706   if (size <= 0) return BAD_INPUT;
707   buffer.reset(size);
708   outcome to_return = receive_from(buffer.access(), size, where_from);
709   // trim the buffer to the actual received size.
710   if (to_return == OKAY)
711     buffer.zap(size, buffer.last());
712   return to_return;
713 }
714
715 outcome spocket::receive_from(abyte *buffer, int &size,
716     internet_address &where_from)
717 {
718 #ifdef DEBUG_SPOCKET
719   FUNCDEF("receive_from");
720 #endif
721   where_from = internet_address();
722   CHECK_BOGUS(NONE_READY);
723   if (_type == CONNECTED) return BAD_INPUT;
724   ENSURE_HEALTH(NO_CONNECTION);
725   int expected = size;
726   size = 0;
727   if (expected <= 0) return BAD_INPUT;
728   GRAB_LOCK;
729   sockaddr from;
730   socklen_t fromlen = sizeof(from);
731   int len = recvfrom(_socket, (char *)buffer, expected, 0, &from, &fromlen);
732   int err = critical_events::system_error();
733   if (!len) return NONE_READY;
734   else if (len < 0) {
735 #ifdef DEBUG_SPOCKET
736     LOG(a_sprintf("actual sys err value=%d", err));
737 #endif
738     if (err == SOCK_EWOULDBLOCK) return NONE_READY;
739     if (err == SOCK_ECONNRESET) return NONE_READY;
740       // this seems to be a necessary windoze kludge; we're not connected
741       // and never were but it says this idiotic garbage about the connection
742       // being reset.
743 #ifdef DEBUG_SPOCKET
744     LOG(astring("The recvfrom failed with an error ")
745         + critical_events::system_error_text(err));
746 #endif
747     if (!connected()) return NO_CONNECTION;
748     return ACCESS_DENIED;
749   }
750   where_from = _stack->convert(from);
751   size = len;
752   return OKAY;
753 }
754
755 } //namespace.
756