0580b1f13c0bdfcdfa2d1cd20398db9be1833d11
[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   FUNCDEF("destructor");
126 #ifdef DEBUG_SPOCKET
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   FUNCDEF("text_form");
152   astring to_return = is_client()? "client" :
153       (is_root_server()? "root-server" : "server");
154   to_return += " spocket: ";
155   if (connected()) {
156     to_return += "connected, ";
157   } else {
158     if (was_connected()) to_return += "unconnected (was once), ";
159     else to_return += "never-connected, ";
160   }
161   to_return += a_sprintf("socket=%u, ", _socket);
162   if (is_root_server()) {
163     to_return += a_sprintf("root-socket=%u, ", _server_socket);
164   }
165   to_return += _where->text_form().s();
166   return to_return;
167 }
168
169 void spocket::bind_client(const internet_address &addr)
170 {
171   _client_bind = true;
172   *_cli_bind = addr;
173 }
174
175 const char *spocket::outcome_name(const outcome &to_name)
176 {
177   switch (to_name.value()) {
178     case NOT_SERVER: return "NOT_SERVER";
179     default: return communication_commons::outcome_name(to_name);
180   }
181 }
182
183 outcome spocket::disconnect()
184 {
185   FUNCDEF("disconnect");
186   RECOGNIZE_DISCO; 
187   if (_socket) {
188 #ifdef DEBUG_SPOCKET
189     LOG(a_sprintf("closing socket %d", _socket));
190 #endif
191     _socks->close(_socket);
192     _socket = 0;
193   }
194   if (_server_socket) {
195 #ifdef DEBUG_SPOCKET
196     LOG(a_sprintf("closing server socket %d", _server_socket));
197 #endif
198     _socks->close(_server_socket);
199     _server_socket = 0;
200   }
201   return OKAY;
202 }
203
204 bool spocket::connected()
205 {
206   FUNCDEF("connected");
207   ENSURE_HEALTH(false);
208
209   if (_type != CONNECTED) return was_connected();
210
211   if (!_socket) return false;
212
213   // do examination on spocket.
214   int sel_mode = 0;
215   GRAB_LOCK;
216
217   try {
218     int ret = _socks->select(_socket, sel_mode);
219     if (ret == 0) {
220       return true;  // we are happy.
221     }
222     if ( (ret & SI_DISCONNECTED) || (ret & SI_ERRONEOUS) ) {
223       RECOGNIZE_DISCO; 
224       return false;
225     }
226     return true;
227   } catch (...) {
228     LOG("caught exception thrown from select, returning false.");
229     return false;
230   }
231 }
232
233 outcome spocket::await_readable(int timeout)
234 {
235   FUNCDEF("await_readable");
236   CHECK_BOGUS(NO_CONNECTION);
237   ENSURE_HEALTH(NO_CONNECTION);
238   GRAB_LOCK;
239   int mode = raw_socket::SELECTING_JUST_READ;
240   int ret = _socks->select(_socket, mode, timeout);
241   if (ret & SI_READABLE) return OKAY;
242     // we found something to report.
243   if (ret & SI_DISCONNECTED) {
244     RECOGNIZE_DISCO; 
245     return NO_CONNECTION;
246   }
247   return _socket? NONE_READY : NO_CONNECTION;
248     // nothing is ready currently.
249 }
250
251 outcome spocket::await_writable(int timeout)
252 {
253   FUNCDEF("await_writable");
254   CHECK_BOGUS(NO_CONNECTION);
255   ENSURE_HEALTH(NO_CONNECTION);
256   GRAB_LOCK;
257   int mode = raw_socket::SELECTING_JUST_WRITE;
258   int ret = _socks->select(_socket, mode, timeout);
259   if (ret & SI_WRITABLE) return OKAY;
260     // we found something to report.
261   if (ret & SI_DISCONNECTED) {
262     RECOGNIZE_DISCO; 
263     return NO_CONNECTION;
264   }
265   return _socket? NONE_READY : NO_CONNECTION;
266     // nothing is ready currently.
267 }
268
269 outcome spocket::connect(int communication_wait)
270 {
271   FUNCDEF("connect");
272   CHECK_BOGUS(NO_CONNECTION);
273   {
274     GRAB_LOCK;  // short lock.
275     if ( (was_connected() && !_client) || _server_socket) {
276 #ifdef DEBUG_SPOCKET
277       LOG("this object was already opened as a server!");
278 #endif
279       return BAD_INPUT;
280     }
281     _client = true;  // set our state now that we're sure this is okay.
282     _was_connected = false;  // reset this, since we're connecting now.
283   }
284
285   if (!_socket) {
286     // the socket was never created (or was cleaned up previously).  this is
287     // where we create the socket so we can communicate.
288 #ifdef DEBUG_SPOCKET
289     LOG(astring("creating socket now for ") + _where->text_form());
290 #endif
291     GRAB_LOCK;
292     int sock_type = SOCK_STREAM;
293     int proto = IPPROTO_TCP;
294
295     if ( (_type == BROADCAST) || (_type == UNICAST) ) {
296       sock_type = SOCK_DGRAM;
297       proto = IPPROTO_IP;
298     }
299     _socket = int(::socket(AF_INET, sock_type, proto));
300     if ( (_socket == basis::un_int(INVALID_SOCKET)) || !_socket) {
301       _socket = 0;
302       LOG("Failed to open the client's connecting spocket.");
303       return ACCESS_DENIED;
304     }
305
306     // mark the spocket for _blocking_ I/O.  we want connect to sit there
307     // until it's connected or returns with an error.
308     _socks->set_non_blocking(_socket, false);
309
310     if (_type == BROADCAST) {
311       if (!_socks->set_broadcast(_socket)) return ACCESS_DENIED;
312         // mark the socket for broadcast capability.
313     }
314
315     if (!_socks->set_reuse_address(_socket)) return ACCESS_DENIED;
316       // mark the socket so we don't get bind errors on in-use conditions.
317   }
318
319   if (_type == CONNECTED) {
320     GRAB_LOCK;
321     // turn on the keepalive timer so that loss of the connection will
322     // eventually be detected by the OS.  the duration that is allowed to
323     // elapse before a dead connection is noticed varies with the operating
324     // system and is not configured at this level.
325     if (!_socks->set_keep_alive(_socket)) {
326 #ifdef DEBUG_SPOCKET
327       LOG("couldn't set watchdog timer on socket.");
328 #endif
329     }
330
331 //hmmm: doesn't this need to be done for bcast too?
332
333     // create the spocket address that we will connect to.
334     if (strlen(_where->hostname)
335 //        && (_where->is_nil_address() 
336 //            || (*_last_resolve < time_stamp(-RESOLVE_INTERVAL) ) ) ) {
337 //
338 //moving to always re-resolving before a connect.  otherwise we have somewhat
339 //hard to predict behavior about when the re-resolve will happen.
340           ) {
341       // we know we need to resolve if the address is NULL_POINTER or if the re-resolve
342       // interval has elapsed.
343       astring full_host;
344       byte_array ip_addr = _stack->full_resolve(_where->hostname, full_host);
345       if (ip_addr.length()) {
346         ip_addr.stuff(internet_address::ADDRESS_SIZE, _where->ip_address);
347         LOG(astring("successfully re-resolved address--") + _where->text_form());
348       }
349       *_last_resolve = time_stamp();  // reset since we just resolved.
350     }
351
352     // special code for forcing a client to bind.
353     if (_client_bind) {
354       sockaddr sock = _stack->convert(*_cli_bind);
355
356 #ifdef DEBUG_SPOCKET
357       LOG(a_sprintf("binding client socket %d to ", _socket)
358           + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
359 #endif
360
361       // now, the socket address is bound to our socket.
362       if (negative(bind(_socket, &sock, sizeof(sock)))) {
363         LOG(a_sprintf("error binding socket %d to ", _socket)
364             + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
365       }
366     }
367
368   } else if ( (_type == BROADCAST) || (_type == UNICAST) ) {
369     // this is the last piece of preparation for a broadcast or unicast socket.
370     // there's no real connection, so we just need to get it bound and ready
371     // to fling packets.
372     GRAB_LOCK;
373     sockaddr sock = _stack->convert(*_where);
374
375 #ifdef DEBUG_SPOCKET
376     LOG(a_sprintf("binding socket %d to ", _socket)
377         + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
378 #endif
379
380     // now, the socket address is bound to our socket.
381     if (negative(bind(_socket, &sock, sizeof(sock)))) {
382       LOG(a_sprintf("error binding socket %d to ", _socket)
383           + inet_ntoa(((sockaddr_in *)&sock)->sin_addr));
384     }
385
386     // that's it for broadcast preparation.  we should be ready.
387     _was_connected = true;
388     return OKAY;
389   }
390
391   // the following is for connected mode only.
392
393   sockaddr sock = _stack->convert(*_where);
394
395   // attempt the connection now.
396
397 //hmmm: error returns are done differently on bsd, right?
398 //hmmm: perhaps hide the base connect in a func that sets our internal
399 //      error variable and then allows comparison to enums we provide.
400
401   time_stamp abort_time(communication_wait);
402
403   bool connected = false;  // did we connect.
404
405   int sock_len = sizeof(sock);
406
407   while (time_stamp() < abort_time) {
408     // make the low-level socket connection.
409     int ret = ::connect(_socket, &sock, sock_len);
410     if (ret != SOCKET_ERROR) {
411       connected = true;
412       _socks->set_non_blocking(_socket, true);
413       break;
414     }
415
416     basis::un_int last_error = critical_events::system_error();
417
418     // if we're already done, then make this look like a normal connect.
419     if (last_error == SOCK_EISCONN) {
420       connected = true;
421       break;
422     }
423
424     if ( (last_error != SOCK_EWOULDBLOCK)
425         && (last_error != SOCK_EINPROGRESS) ) {
426       // this seems like a real error here.
427 #ifdef DEBUG_SPOCKET
428       LOG(a_sprintf("Connect failed (error %s or %d) on address:",
429           critical_events::system_error_text(last_error).s(), last_error)
430           + _where->text_form());
431 #endif
432       if (last_error == SOCK_ECONNREFUSED) return NO_ANSWER;
433 //hmmm: fix more of the possibilities to be sensible outcomes?
434       return ACCESS_DENIED;
435     }
436
437     if (time_stamp() >= abort_time) break;  // skip before sleeping if T.O.
438
439     // snooze for a bit before trying again.
440     time_control::sleep_ms(10);
441   }
442
443   if (connected) {
444 #ifdef DEBUG_SPOCKET
445     LOG(a_sprintf("socket %d connected to server.", _socket));
446 #endif
447     GRAB_LOCK;  // short lock.
448     _was_connected = true;
449     return OKAY;
450   }
451
452   return TIMED_OUT;
453 }
454
455 outcome spocket::accept(spocket * &sock, bool wait)
456 {
457   FUNCDEF("accept");
458   CHECK_BOGUS(NO_CONNECTION);
459   if (_type != CONNECTED) return BAD_INPUT;
460
461   // we don't lock in here; we should not be locking on the server socket.
462
463   sock = NULL_POINTER;  // reset.
464
465   if (_socket) {
466 #ifdef DEBUG_SPOCKET
467     LOG("tried to accept on a client spocket.");
468 #endif
469     return NOT_SERVER;
470   }
471   _client = false;
472
473   if (!_server_socket) {
474     _server_socket = int(::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
475 #ifdef DEBUG_SPOCKET
476     LOG(a_sprintf("srv sock is %d", _server_socket));
477     LOG(astring("creating server socket now for ") + _where->text_form());
478 #endif
479
480     if (_server_socket == basis::un_int(INVALID_SOCKET)) {
481       LOG("Failed to open the serving spocket.");
482       return BAD_INPUT;
483     }
484
485     // mark the socket so we don't get bind errors on in-use conditions.
486     if (!_socks->set_reuse_address(_server_socket)) 
487       LOG("Failed to mark the socket for re-use.");
488
489     // create the spocket address for where we exist.
490     sockaddr sock = _stack->convert(*_where);
491
492     // now, the spocket address is bound to our spocket.
493     int sock_len = sizeof(sock);
494     if (bind(_server_socket, (sockaddr *)&sock, sock_len) < 0) {
495       LOG(astring("Error on bind of ") + critical_events::system_error_text(critical_events::system_error()));
496       _socks->close(_server_socket);
497       return ACCESS_DENIED;
498     }
499
500     // now listen for a connection on our spocket.
501     if (listen(_server_socket, PENDING_CONNECTIONS_ALLOWED) < 0) {
502       LOG(astring("Listen failed with error of ")
503           + critical_events::system_error_text(critical_events::system_error()));
504       _socks->close(_server_socket);
505       return ACCESS_DENIED;
506     }
507   }
508
509   // do the kind of accept they want; either block on it or don't.
510   // since our server socket is never used for sends or receives, we pretty
511   // much control it completely and this is safe.
512   if (!wait) {
513     _socks->set_non_blocking(_server_socket, true);
514       // mark our socket as non-blocking so we don't get stuck in accepts.
515   } else {
516     _socks->set_non_blocking(_server_socket, false);
517       // mark our socket as blocking; we will be paused until accept occurs.
518   }
519
520   // now try accepting a connection on the spocket.
521   sockaddr new_sock;
522   socklen_t sock_len = sizeof(new_sock);
523   basis::un_int accepted = int(::accept(_server_socket, &new_sock, &sock_len));
524   int error = critical_events::system_error();
525   if (!accepted || (accepted == INVALID_SOCKET)) {
526     if (error == SOCK_EWOULDBLOCK) return NO_CONNECTION;
527 #ifdef DEBUG_SPOCKET
528     LOG(astring("Accept got no client, with an error of ")
529         + critical_events::system_error_text(error));
530 #endif
531     return ACCESS_DENIED;
532   }
533
534   // mark the new spocket for non-blocking I/O.
535   _socks->set_non_blocking(accepted, true);
536
537 //move to socks object!
538   int sock_hop = 1;
539   if (setsockopt(accepted, SOL_SOCKET, SO_KEEPALIVE, (char *)&sock_hop,
540       sizeof(sock_hop)) < 0) {
541 #ifdef DEBUG_SPOCKET
542     LOG("couldn't set watchdog timer on socket.");
543 #endif
544   }
545
546 #ifdef DEBUG_SPOCKET
547   LOG(astring("accepted a client on our socket: ") + _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   // create the spocket address that we will connect to.
555   sock = new spocket(*_where);
556   *sock->_remote = _stack->convert(new_sock);
557   sock->_socket = accepted;
558   sock->_server_socket = 0;  // reset to avoid whacking.
559   sock->_was_connected = true;
560   return OKAY;
561 }
562
563 outcome spocket::send(const byte_array &to_send, int &len_sent)
564 {
565   return send(to_send.observe(), to_send.length(), len_sent);
566 }
567
568 outcome spocket::send(const abyte *buffer, int size, int &len_sent)
569 {
570   FUNCDEF("send");
571   CHECK_BOGUS(OKAY);
572   if (_type != CONNECTED) return BAD_INPUT;
573   GRAB_LOCK;
574   ENSURE_HEALTH(NO_CONNECTION);
575
576   len_sent = ::send(_socket, (char *)buffer, size, 0);
577   int error_code = critical_events::system_error();
578   if (!len_sent) {
579 #ifdef DEBUG_SPOCKET
580     LOG("No data went out on the spocket.");
581 #endif
582     return PARTIAL;
583   }
584   if (len_sent == SOCKET_ERROR) {
585     if (error_code == SOCK_EWOULDBLOCK) {
586 #ifdef DEBUG_SPOCKET
587       LOG("would block, will try later...");
588       if (len_sent > 0)
589         LOG("HEY HEY!  some was sent but we were not counting it!!!");
590 #endif
591       return NONE_READY;
592     }
593 #ifdef DEBUG_SPOCKET
594     LOG(astring("Error ") + critical_events::system_error_text(error_code)
595         + " occurred during the send!");
596 #endif
597     if (!connected()) return NO_CONNECTION;
598 #ifdef DEBUG_SPOCKET
599     LOG(a_sprintf("forcing disconnect on socket %d.", _socket));
600 #endif
601     // we're trying this new approach here...  we found that the socket doesn't
602     // really know that it got disconnected in some circumstances.
603     disconnect();
604     return ACCESS_DENIED;
605   }
606   if (len_sent != size) {
607     // only sent part of the buffer.
608 #ifdef DEBUG_SPOCKET
609     LOG(a_sprintf("sent %d bytes out of %d.", len_sent, size));
610 #endif
611     return PARTIAL;
612   }
613
614   return OKAY;
615 }
616
617 outcome spocket::send_to(const internet_address &where_to,
618     const byte_array &to_send, int &len_sent)
619 {
620   return send_to(where_to, to_send.observe(), to_send.length(), len_sent);
621 }
622
623 outcome spocket::send_to(const internet_address &where_to, const abyte *to_send,
624     int size, int &len_sent)
625 {
626   FUNCDEF("send_to");
627   CHECK_BOGUS(OKAY);
628   if (_type == CONNECTED) return BAD_INPUT;
629   sockaddr dest = _stack->convert(where_to);
630   int ret = sendto(_socket, (char *)to_send, size, 0, &dest, sizeof(dest));
631   int error = critical_events::system_error();
632   if (ret < 0) {
633     if (error == SOCK_EWOULDBLOCK) return NONE_READY;  // no buffer space?
634     LOG(astring("failed to send packet; error ")
635         + _stack->tcpip_error_name(error));
636     return ACCESS_DENIED;
637   }
638   if (ret != size) {
639     LOG(astring("didn't send whole datagram!"));
640   }
641   len_sent = ret;
642   return OKAY;
643 }
644
645 outcome spocket::receive(byte_array &buffer, int &size)
646 {
647   FUNCDEF("receive");
648   CHECK_BOGUS(NONE_READY);
649   if (_type != CONNECTED) return BAD_INPUT;
650   if (size <= 0) return BAD_INPUT;
651   buffer.reset(size);
652   outcome to_return = receive(buffer.access(), size);
653   // trim the buffer to the actual received size.
654   if (to_return == OKAY)
655     buffer.zap(size, buffer.last());
656   return to_return;
657 }
658
659 outcome spocket::receive(abyte *buffer, int &size)
660 {
661   FUNCDEF("receive");
662   CHECK_BOGUS(NONE_READY);
663   if (_type != CONNECTED) return BAD_INPUT;
664   ENSURE_HEALTH(NO_CONNECTION);
665   int expected = size;
666   size = 0;
667   if (expected <= 0) return BAD_INPUT;
668   GRAB_LOCK;
669   int len = recv(_socket, (char *)buffer, expected, 0);
670   if (!len) {
671     // check to make sure we're not disconnected.
672     int ret = _socks->select(_socket, raw_socket::SELECTING_JUST_READ);
673     if (ret & SI_DISCONNECTED) {
674       RECOGNIZE_DISCO; 
675       return NO_CONNECTION;
676     }
677     // seems like more normal absence of data.
678     return NONE_READY;
679   } else if (len < 0) {
680     if (critical_events::system_error() == SOCK_EWOULDBLOCK) return NONE_READY;
681 #ifdef DEBUG_SPOCKET
682     LOG(astring("The receive failed with an error ")
683         + critical_events::system_error_text(critical_events::system_error()));
684 #endif
685     if (!connected()) return NO_CONNECTION;
686     return ACCESS_DENIED;
687   }
688   size = len;
689   return OKAY;
690 }
691
692 outcome spocket::receive_from(byte_array &buffer, int &size,
693     internet_address &where_from)
694 {
695   FUNCDEF("receive_from");
696   where_from = internet_address();
697   CHECK_BOGUS(NONE_READY);
698   if (_type == CONNECTED) return BAD_INPUT;
699   if (size <= 0) return BAD_INPUT;
700   buffer.reset(size);
701   outcome to_return = receive_from(buffer.access(), size, where_from);
702   // trim the buffer to the actual received size.
703   if (to_return == OKAY)
704     buffer.zap(size, buffer.last());
705   return to_return;
706 }
707
708 outcome spocket::receive_from(abyte *buffer, int &size,
709     internet_address &where_from)
710 {
711   FUNCDEF("receive_from");
712   where_from = internet_address();
713   CHECK_BOGUS(NONE_READY);
714   if (_type == CONNECTED) return BAD_INPUT;
715   ENSURE_HEALTH(NO_CONNECTION);
716   int expected = size;
717   size = 0;
718   if (expected <= 0) return BAD_INPUT;
719   GRAB_LOCK;
720   sockaddr from;
721   socklen_t fromlen = sizeof(from);
722   int len = recvfrom(_socket, (char *)buffer, expected, 0, &from, &fromlen);
723   int err = critical_events::system_error();
724   if (!len) return NONE_READY;
725   else if (len < 0) {
726 #ifdef DEBUG_SPOCKET
727     LOG(a_sprintf("actual sys err value=%d", err));
728 #endif
729     if (err == SOCK_EWOULDBLOCK) return NONE_READY;
730     if (err == SOCK_ECONNRESET) return NONE_READY;
731       // this seems to be a necessary windoze kludge; we're not connected
732       // and never were but it says this idiotic garbage about the connection
733       // being reset.
734 #ifdef DEBUG_SPOCKET
735     LOG(astring("The recvfrom failed with an error ")
736         + critical_events::system_error_text(err));
737 #endif
738     if (!connected()) return NO_CONNECTION;
739     return ACCESS_DENIED;
740   }
741   where_from = _stack->convert(from);
742   size = len;
743   return OKAY;
744 }
745
746 } //namespace.
747