3 #include <sys/socket.h>
4 #include <sys/select.h>
11 #include "socketbase.h"
13 #include <lib/base/ebase.h>
14 #include <lib/base/eerror.h>
16 int eSocketBase::select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
19 fd_set rset, wset, xset;
22 /* make a backup of all fd_set's and timeval struct */
23 if (readfds) rset = *readfds;
24 if (writefds) wset = *writefds;
25 if (exceptfds) xset = *exceptfds;
32 /* make gcc happy... */
33 timerclear(&interval);
38 retval = ::select(maxfd, readfds, writefds, exceptfds, timeout);
42 /* restore the backup before we continue */
43 if (readfds) *readfds = rset;
44 if (writefds) *writefds = wset;
45 if (exceptfds) *exceptfds = xset;
46 if (timeout) *timeout = interval;
47 if (errno == EINTR) continue;
48 eDebug("eSocketBase::select error (%m)");
57 ssize_t eSocketBase::singleRead(int fd, void *buf, size_t count)
62 retval = ::read(fd, buf, count);
65 if (errno == EINTR) continue;
66 eDebug("eSocketBase::singleRead error (%m)");
72 ssize_t eSocketBase::timedRead(int fd, void *buf, size_t count, int initialtimeout, int interbytetimeout)
75 struct timeval timeout;
79 while (totalread < count)
85 timeout.tv_sec = initialtimeout/1000;
86 timeout.tv_usec = (initialtimeout%1000) * 1000;
90 timeout.tv_sec = interbytetimeout / 1000;
91 timeout.tv_usec = (interbytetimeout%1000) * 1000;
93 if ((result = select(fd + 1, &rset, NULL, NULL, &timeout)) < 0) return -1; /* error */
94 if (result == 0) break;
95 if ((result = singleRead(fd, ((char*)buf) + totalread, count - totalread)) < 0)
99 if (result == 0) break;
105 ssize_t eSocketBase::readLine(int fd, char** buffer, size_t* bufsize)
113 char *newbuf = (char*)realloc(*buffer, (*bufsize)+1024);
117 *bufsize = (*bufsize) + 1024;
119 result = timedRead(fd, (*buffer) + i, 1, 3000, 100);
120 if (result <= 0 || (*buffer)[i] == '\n')
123 return result <= 0 ? -1 : i;
125 if ((*buffer)[i] != '\r') i++;
130 int eSocketBase::connect(const char *hostname, int port, int timeoutsec)
133 std::vector<struct addrinfo *> addresses;
134 struct addrinfo *info = NULL;
135 struct addrinfo hints;
136 memset(&hints, 0, sizeof(hints));
137 hints.ai_family = AF_UNSPEC; /* both ipv4 and ipv6 */
138 hints.ai_socktype = SOCK_STREAM;
139 hints.ai_protocol = 0; /* any */
141 hints.ai_flags = AI_ADDRCONFIG; /* only return ipv6 if we have an ipv6 address ourselves, and ipv4 if we have an ipv4 address ourselves */
143 hints.ai_flags = 0; /* we have only IPV4 support, if AI_ADDRCONFIG is not available */
146 /* this is suboptimal, but the alternative would require us to mess with the memberdata of an 'abstract' addressinfo struct */
147 snprintf(portstring, sizeof(portstring), "%d", port);
148 if (getaddrinfo(hostname, portstring, &hints, &info) || !info) return -1;
149 struct addrinfo *ptr = info;
152 addresses.push_back(ptr);
156 for (unsigned int i = 0; i < addresses.size(); i++)
158 sd = ::socket(addresses[i]->ai_family, addresses[i]->ai_socktype, addresses[i]->ai_protocol);
161 bool setblocking = false;
162 if ((flags = fcntl(sd, F_GETFL, 0)) < 0)
168 if (!(flags & O_NONBLOCK))
170 /* set socket nonblocking, to allow for our own timeout on a nonblocking connect */
172 if (fcntl(sd, F_SETFL, flags) < 0)
178 /* remember to restore O_NONBLOCK when we're connected */
184 connectresult = ::connect(sd, addresses[i]->ai_addr, addresses[i]->ai_addrlen);
185 if (connectresult < 0)
187 if (errno == EINTR || errno == EINPROGRESS)
190 socklen_t len = sizeof(error);
196 timeout.tv_sec = timeoutsec;
199 if (select(sd + 1, NULL, &wset, NULL, &timeout) <= 0) break;
201 if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) break;
204 /* we are connected */
211 if (connectresult < 0)
219 /* set socket blocking again */
220 flags &= ~O_NONBLOCK;
221 if (fcntl(sd, F_SETFL, flags) < 0)
232 setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (char*)&val, sizeof(val));
234 /* we have a working connection */
242 ssize_t eSocketBase::writeAll(int fd, const void *buf, size_t count)
245 char *ptr = (char*)buf;
246 size_t handledcount = 0;
247 while (handledcount < count)
249 retval = ::write(fd, &ptr[handledcount], count - handledcount);
251 if (retval == 0) return -1;
254 if (errno == EINTR) continue;
255 eDebug("eSocketBase::writeAll error (%m)");
258 handledcount += retval;