1 | // |
2 | // SocketImpl.cpp |
3 | // |
4 | // Library: Net |
5 | // Package: Sockets |
6 | // Module: SocketImpl |
7 | // |
8 | // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Net/SocketImpl.h" |
16 | #include "Poco/Net/NetException.h" |
17 | #include "Poco/Net/StreamSocketImpl.h" |
18 | #include "Poco/NumberFormatter.h" |
19 | #include "Poco/Timestamp.h" |
20 | #include <string.h> // FD_SET needs memset on some platforms, so we can't use <cstring> |
21 | #if defined(POCO_HAVE_FD_EPOLL) |
22 | #include <sys/epoll.h> |
23 | #elif defined(POCO_HAVE_FD_POLL) |
24 | #include <poll.h> |
25 | #endif |
26 | |
27 | |
28 | #if defined(sun) || defined(__sun) || defined(__sun__) |
29 | #include <unistd.h> |
30 | #include <stropts.h> |
31 | #endif |
32 | |
33 | |
34 | #ifdef POCO_OS_FAMILY_WINDOWS |
35 | #include <Windows.h> |
36 | #endif |
37 | |
38 | |
39 | using Poco::IOException; |
40 | using Poco::TimeoutException; |
41 | using Poco::InvalidArgumentException; |
42 | using Poco::NumberFormatter; |
43 | using Poco::Timespan; |
44 | |
45 | |
46 | namespace Poco { |
47 | namespace Net { |
48 | |
49 | |
50 | bool checkIsBrokenTimeout() |
51 | { |
52 | #if defined(POCO_BROKEN_TIMEOUTS) |
53 | return true; |
54 | #elif defined(POCO_OS_FAMILY_WINDOWS) |
55 | // on Windows 7 and lower, socket timeouts have a minimum of 500ms, use poll for timeouts on this case |
56 | // https://social.msdn.microsoft.com/Forums/en-US/76620f6d-22b1-4872-aaf0-833204f3f867/minimum-timeout-value-for-sorcvtimeo |
57 | OSVERSIONINFO vi; |
58 | vi.dwOSVersionInfoSize = sizeof(vi); |
59 | if (GetVersionEx(&vi) == 0) return true; //throw SystemException("Cannot get OS version information"); |
60 | return vi.dwMajorVersion < 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion < 2); |
61 | #endif |
62 | return false; |
63 | } |
64 | |
65 | |
66 | SocketImpl::SocketImpl(): |
67 | _sockfd(POCO_INVALID_SOCKET), |
68 | _blocking(true), |
69 | _isBrokenTimeout(checkIsBrokenTimeout()) |
70 | { |
71 | } |
72 | |
73 | |
74 | SocketImpl::SocketImpl(poco_socket_t sockfd): |
75 | _sockfd(sockfd), |
76 | _blocking(true), |
77 | _isBrokenTimeout(checkIsBrokenTimeout()) |
78 | { |
79 | } |
80 | |
81 | |
82 | SocketImpl::~SocketImpl() |
83 | { |
84 | close(); |
85 | } |
86 | |
87 | |
88 | SocketImpl* SocketImpl::acceptConnection(SocketAddress& clientAddr) |
89 | { |
90 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
91 | |
92 | char buffer[SocketAddress::MAX_ADDRESS_LENGTH]; |
93 | struct sockaddr* pSA = reinterpret_cast<struct sockaddr*>(buffer); |
94 | poco_socklen_t saLen = sizeof(buffer); |
95 | poco_socket_t sd; |
96 | do |
97 | { |
98 | sd = ::accept(_sockfd, pSA, &saLen); |
99 | } |
100 | while (sd == POCO_INVALID_SOCKET && lastError() == POCO_EINTR); |
101 | if (sd != POCO_INVALID_SOCKET) |
102 | { |
103 | clientAddr = SocketAddress(pSA, saLen); |
104 | return new StreamSocketImpl(sd); |
105 | } |
106 | error(); // will throw |
107 | return 0; |
108 | } |
109 | |
110 | |
111 | void SocketImpl::connect(const SocketAddress& address) |
112 | { |
113 | if (_sockfd == POCO_INVALID_SOCKET) |
114 | { |
115 | init(address.af()); |
116 | } |
117 | int rc; |
118 | do |
119 | { |
120 | #if defined(POCO_VXWORKS) |
121 | rc = ::connect(_sockfd, (sockaddr*) address.addr(), address.length()); |
122 | #else |
123 | rc = ::connect(_sockfd, address.addr(), address.length()); |
124 | #endif |
125 | } |
126 | while (rc != 0 && lastError() == POCO_EINTR); |
127 | if (rc != 0) |
128 | { |
129 | int err = lastError(); |
130 | error(err, address.toString()); |
131 | } |
132 | } |
133 | |
134 | |
135 | void SocketImpl::connect(const SocketAddress& address, const Poco::Timespan& timeout) |
136 | { |
137 | if (_sockfd == POCO_INVALID_SOCKET) |
138 | { |
139 | init(address.af()); |
140 | } |
141 | setBlocking(false); |
142 | try |
143 | { |
144 | #if defined(POCO_VXWORKS) |
145 | int rc = ::connect(_sockfd, (sockaddr*) address.addr(), address.length()); |
146 | #else |
147 | int rc = ::connect(_sockfd, address.addr(), address.length()); |
148 | #endif |
149 | if (rc != 0) |
150 | { |
151 | int err = lastError(); |
152 | if (err != POCO_EINPROGRESS && err != POCO_EWOULDBLOCK) |
153 | error(err, address.toString()); |
154 | if (!poll(timeout, SELECT_READ | SELECT_WRITE | SELECT_ERROR)) |
155 | throw Poco::TimeoutException("connect timed out" , address.toString()); |
156 | err = socketError(); |
157 | if (err != 0) error(err); |
158 | } |
159 | } |
160 | catch (Poco::Exception&) |
161 | { |
162 | setBlocking(true); |
163 | throw; |
164 | } |
165 | setBlocking(true); |
166 | } |
167 | |
168 | |
169 | void SocketImpl::connectNB(const SocketAddress& address) |
170 | { |
171 | if (_sockfd == POCO_INVALID_SOCKET) |
172 | { |
173 | init(address.af()); |
174 | } |
175 | setBlocking(false); |
176 | #if defined(POCO_VXWORKS) |
177 | int rc = ::connect(_sockfd, (sockaddr*) address.addr(), address.length()); |
178 | #else |
179 | int rc = ::connect(_sockfd, address.addr(), address.length()); |
180 | #endif |
181 | if (rc != 0) |
182 | { |
183 | int err = lastError(); |
184 | if (err != POCO_EINPROGRESS && err != POCO_EWOULDBLOCK) |
185 | error(err, address.toString()); |
186 | } |
187 | } |
188 | |
189 | |
190 | void SocketImpl::bind(const SocketAddress& address, bool reuseAddress) |
191 | { |
192 | bind(address, reuseAddress, true); |
193 | } |
194 | |
195 | |
196 | void SocketImpl::bind(const SocketAddress& address, bool reuseAddress, bool reusePort) |
197 | { |
198 | if (_sockfd == POCO_INVALID_SOCKET) |
199 | { |
200 | init(address.af()); |
201 | } |
202 | if (reuseAddress) |
203 | setReuseAddress(true); |
204 | if (reusePort) |
205 | setReusePort(true); |
206 | #if defined(POCO_VXWORKS) |
207 | int rc = ::bind(_sockfd, (sockaddr*) address.addr(), address.length()); |
208 | #else |
209 | int rc = ::bind(_sockfd, address.addr(), address.length()); |
210 | #endif |
211 | if (rc != 0) error(address.toString()); |
212 | } |
213 | |
214 | |
215 | void SocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool ipV6Only) |
216 | { |
217 | bind6(address, reuseAddress, true, ipV6Only); |
218 | } |
219 | |
220 | |
221 | void SocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only) |
222 | { |
223 | #if defined(POCO_HAVE_IPv6) |
224 | if (address.family() != SocketAddress::IPv6) |
225 | throw Poco::InvalidArgumentException("SocketAddress must be an IPv6 address" ); |
226 | |
227 | if (_sockfd == POCO_INVALID_SOCKET) |
228 | { |
229 | init(address.af()); |
230 | } |
231 | #ifdef IPV6_V6ONLY |
232 | setOption(IPPROTO_IPV6, IPV6_V6ONLY, ipV6Only ? 1 : 0); |
233 | #else |
234 | if (ipV6Only) throw Poco::NotImplementedException("IPV6_V6ONLY not defined." ); |
235 | #endif |
236 | if (reuseAddress) |
237 | setReuseAddress(true); |
238 | if (reusePort) |
239 | setReusePort(true); |
240 | int rc = ::bind(_sockfd, address.addr(), address.length()); |
241 | if (rc != 0) error(address.toString()); |
242 | #else |
243 | throw Poco::NotImplementedException("No IPv6 support available" ); |
244 | #endif |
245 | } |
246 | |
247 | |
248 | void SocketImpl::listen(int backlog) |
249 | { |
250 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
251 | |
252 | int rc = ::listen(_sockfd, backlog); |
253 | if (rc != 0) error(); |
254 | } |
255 | |
256 | |
257 | void SocketImpl::close() |
258 | { |
259 | if (_sockfd != POCO_INVALID_SOCKET) |
260 | { |
261 | poco_closesocket(_sockfd); |
262 | _sockfd = POCO_INVALID_SOCKET; |
263 | } |
264 | } |
265 | |
266 | |
267 | void SocketImpl::shutdownReceive() |
268 | { |
269 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
270 | |
271 | int rc = ::shutdown(_sockfd, 0); |
272 | if (rc != 0) error(); |
273 | } |
274 | |
275 | |
276 | void SocketImpl::shutdownSend() |
277 | { |
278 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
279 | |
280 | int rc = ::shutdown(_sockfd, 1); |
281 | if (rc != 0) error(); |
282 | } |
283 | |
284 | |
285 | void SocketImpl::shutdown() |
286 | { |
287 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
288 | |
289 | int rc = ::shutdown(_sockfd, 2); |
290 | if (rc != 0) error(); |
291 | } |
292 | |
293 | |
294 | int SocketImpl::sendBytes(const void* buffer, int length, int flags) |
295 | { |
296 | if (_isBrokenTimeout) |
297 | { |
298 | if (_sndTimeout.totalMicroseconds() != 0) |
299 | { |
300 | if (!poll(_sndTimeout, SELECT_WRITE)) |
301 | throw TimeoutException(); |
302 | } |
303 | } |
304 | |
305 | int rc; |
306 | do |
307 | { |
308 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
309 | rc = ::send(_sockfd, reinterpret_cast<const char*>(buffer), length, flags); |
310 | } |
311 | while (_blocking && rc < 0 && lastError() == POCO_EINTR); |
312 | if (rc < 0) |
313 | { |
314 | int err = lastError(); |
315 | if (err == POCO_EAGAIN || err == POCO_ETIMEDOUT) |
316 | throw TimeoutException(); |
317 | else |
318 | error(err); |
319 | } |
320 | return rc; |
321 | } |
322 | |
323 | |
324 | int SocketImpl::receiveBytes(void* buffer, int length, int flags) |
325 | { |
326 | if (_isBrokenTimeout) |
327 | { |
328 | if (_recvTimeout.totalMicroseconds() != 0) |
329 | { |
330 | if (!poll(_recvTimeout, SELECT_READ)) |
331 | throw TimeoutException(); |
332 | } |
333 | } |
334 | |
335 | int rc; |
336 | do |
337 | { |
338 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
339 | rc = ::recv(_sockfd, reinterpret_cast<char*>(buffer), length, flags); |
340 | } |
341 | while (_blocking && rc < 0 && lastError() == POCO_EINTR); |
342 | if (rc < 0) |
343 | { |
344 | int err = lastError(); |
345 | if (err == POCO_EAGAIN && !_blocking) |
346 | ; |
347 | else if (err == POCO_EAGAIN || err == POCO_ETIMEDOUT) |
348 | throw TimeoutException(err); |
349 | else |
350 | error(err); |
351 | } |
352 | return rc; |
353 | } |
354 | |
355 | |
356 | int SocketImpl::sendTo(const void* buffer, int length, const SocketAddress& address, int flags) |
357 | { |
358 | int rc; |
359 | do |
360 | { |
361 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
362 | #if defined(POCO_VXWORKS) |
363 | rc = ::sendto(_sockfd, (char*) buffer, length, flags, (sockaddr*) address.addr(), address.length()); |
364 | #else |
365 | rc = ::sendto(_sockfd, reinterpret_cast<const char*>(buffer), length, flags, address.addr(), address.length()); |
366 | #endif |
367 | } |
368 | while (_blocking && rc < 0 && lastError() == POCO_EINTR); |
369 | if (rc < 0) error(); |
370 | return rc; |
371 | } |
372 | |
373 | |
374 | int SocketImpl::receiveFrom(void* buffer, int length, SocketAddress& address, int flags) |
375 | { |
376 | if (_isBrokenTimeout) |
377 | { |
378 | if (_recvTimeout.totalMicroseconds() != 0) |
379 | { |
380 | if (!poll(_recvTimeout, SELECT_READ)) |
381 | throw TimeoutException(); |
382 | } |
383 | } |
384 | |
385 | char abuffer[SocketAddress::MAX_ADDRESS_LENGTH]; |
386 | struct sockaddr* pSA = reinterpret_cast<struct sockaddr*>(abuffer); |
387 | poco_socklen_t saLen = sizeof(abuffer); |
388 | int rc; |
389 | do |
390 | { |
391 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
392 | rc = ::recvfrom(_sockfd, reinterpret_cast<char*>(buffer), length, flags, pSA, &saLen); |
393 | } |
394 | while (_blocking && rc < 0 && lastError() == POCO_EINTR); |
395 | if (rc >= 0) |
396 | { |
397 | address = SocketAddress(pSA, saLen); |
398 | } |
399 | else |
400 | { |
401 | int err = lastError(); |
402 | if (err == POCO_EAGAIN && !_blocking) |
403 | ; |
404 | else if (err == POCO_EAGAIN || err == POCO_ETIMEDOUT) |
405 | throw TimeoutException(err); |
406 | else |
407 | error(err); |
408 | } |
409 | return rc; |
410 | } |
411 | |
412 | |
413 | void SocketImpl::sendUrgent(unsigned char data) |
414 | { |
415 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
416 | |
417 | int rc = ::send(_sockfd, reinterpret_cast<const char*>(&data), sizeof(data), MSG_OOB); |
418 | if (rc < 0) error(); |
419 | } |
420 | |
421 | |
422 | int SocketImpl::available() |
423 | { |
424 | int result; |
425 | ioctl(FIONREAD, result); |
426 | return result; |
427 | } |
428 | |
429 | |
430 | bool SocketImpl::secure() const |
431 | { |
432 | return false; |
433 | } |
434 | |
435 | |
436 | bool SocketImpl::poll(const Poco::Timespan& timeout, int mode) |
437 | { |
438 | poco_socket_t sockfd = _sockfd; |
439 | if (sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
440 | |
441 | #if defined(POCO_HAVE_FD_EPOLL) |
442 | |
443 | int epollfd = epoll_create(1); |
444 | if (epollfd < 0) |
445 | { |
446 | error("Can't create epoll queue" ); |
447 | } |
448 | |
449 | struct epoll_event evin; |
450 | memset(&evin, 0, sizeof(evin)); |
451 | |
452 | if (mode & SELECT_READ) |
453 | evin.events |= EPOLLIN; |
454 | if (mode & SELECT_WRITE) |
455 | evin.events |= EPOLLOUT; |
456 | if (mode & SELECT_ERROR) |
457 | evin.events |= EPOLLERR; |
458 | |
459 | if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &evin) < 0) |
460 | { |
461 | ::close(epollfd); |
462 | error("Can't insert socket to epoll queue" ); |
463 | } |
464 | |
465 | Poco::Timespan remainingTime(timeout); |
466 | int rc; |
467 | do |
468 | { |
469 | struct epoll_event evout; |
470 | memset(&evout, 0, sizeof(evout)); |
471 | |
472 | Poco::Timestamp start; |
473 | rc = epoll_wait(epollfd, &evout, 1, remainingTime.totalMilliseconds()); |
474 | if (rc < 0 && lastError() == POCO_EINTR) |
475 | { |
476 | Poco::Timestamp end; |
477 | Poco::Timespan waited = end - start; |
478 | if (waited < remainingTime) |
479 | remainingTime -= waited; |
480 | else |
481 | remainingTime = 0; |
482 | } |
483 | } |
484 | while (rc < 0 && lastError() == POCO_EINTR); |
485 | |
486 | ::close(epollfd); |
487 | if (rc < 0) error(); |
488 | return rc > 0; |
489 | |
490 | #elif defined(POCO_HAVE_FD_POLL) |
491 | |
492 | pollfd pollBuf; |
493 | |
494 | memset(&pollBuf, 0, sizeof(pollfd)); |
495 | pollBuf.fd = _sockfd; |
496 | if (mode & SELECT_READ) pollBuf.events |= POLLIN; |
497 | if (mode & SELECT_WRITE) pollBuf.events |= POLLOUT; |
498 | |
499 | Poco::Timespan remainingTime(timeout); |
500 | int rc; |
501 | do |
502 | { |
503 | Poco::Timestamp start; |
504 | rc = ::poll(&pollBuf, 1, remainingTime.totalMilliseconds()); |
505 | |
506 | if (rc < 0 && lastError() == POCO_EINTR) |
507 | { |
508 | Poco::Timestamp end; |
509 | Poco::Timespan waited = end - start; |
510 | if (waited < remainingTime) |
511 | remainingTime -= waited; |
512 | else |
513 | remainingTime = 0; |
514 | } |
515 | } |
516 | while (rc < 0 && lastError() == POCO_EINTR); |
517 | if (rc < 0) error(); |
518 | return rc > 0; |
519 | |
520 | #else |
521 | |
522 | fd_set fdRead; |
523 | fd_set fdWrite; |
524 | fd_set fdExcept; |
525 | FD_ZERO(&fdRead); |
526 | FD_ZERO(&fdWrite); |
527 | FD_ZERO(&fdExcept); |
528 | if (mode & SELECT_READ) |
529 | { |
530 | FD_SET(sockfd, &fdRead); |
531 | } |
532 | if (mode & SELECT_WRITE) |
533 | { |
534 | FD_SET(sockfd, &fdWrite); |
535 | } |
536 | if (mode & SELECT_ERROR) |
537 | { |
538 | FD_SET(sockfd, &fdExcept); |
539 | } |
540 | Poco::Timespan remainingTime(timeout); |
541 | int errorCode = POCO_ENOERR; |
542 | int rc; |
543 | do |
544 | { |
545 | struct timeval tv; |
546 | tv.tv_sec = (long) remainingTime.totalSeconds(); |
547 | tv.tv_usec = (long) remainingTime.useconds(); |
548 | Poco::Timestamp start; |
549 | rc = ::select(int(sockfd) + 1, &fdRead, &fdWrite, &fdExcept, &tv); |
550 | if (rc < 0 && (errorCode = lastError()) == POCO_EINTR) |
551 | { |
552 | Poco::Timestamp end; |
553 | Poco::Timespan waited = end - start; |
554 | if (waited < remainingTime) |
555 | remainingTime -= waited; |
556 | else |
557 | remainingTime = 0; |
558 | } |
559 | } |
560 | while (rc < 0 && errorCode == POCO_EINTR); |
561 | if (rc < 0) error(errorCode); |
562 | return rc > 0; |
563 | |
564 | #endif // POCO_HAVE_FD_EPOLL |
565 | } |
566 | |
567 | |
568 | void SocketImpl::setSendBufferSize(int size) |
569 | { |
570 | setOption(SOL_SOCKET, SO_SNDBUF, size); |
571 | } |
572 | |
573 | |
574 | int SocketImpl::getSendBufferSize() |
575 | { |
576 | int result; |
577 | getOption(SOL_SOCKET, SO_SNDBUF, result); |
578 | return result; |
579 | } |
580 | |
581 | |
582 | void SocketImpl::setReceiveBufferSize(int size) |
583 | { |
584 | setOption(SOL_SOCKET, SO_RCVBUF, size); |
585 | } |
586 | |
587 | |
588 | int SocketImpl::getReceiveBufferSize() |
589 | { |
590 | int result; |
591 | getOption(SOL_SOCKET, SO_RCVBUF, result); |
592 | return result; |
593 | } |
594 | |
595 | |
596 | void SocketImpl::setSendTimeout(const Poco::Timespan& timeout) |
597 | { |
598 | #if defined(_WIN32) |
599 | int value = (int) timeout.totalMilliseconds(); |
600 | setOption(SOL_SOCKET, SO_SNDTIMEO, value); |
601 | #else |
602 | setOption(SOL_SOCKET, SO_SNDTIMEO, timeout); |
603 | #endif |
604 | _sndTimeout = timeout; |
605 | } |
606 | |
607 | |
608 | Poco::Timespan SocketImpl::getSendTimeout() |
609 | { |
610 | Timespan result; |
611 | #if defined(_WIN32) && !defined(POCO_BROKEN_TIMEOUTS) |
612 | int value; |
613 | getOption(SOL_SOCKET, SO_SNDTIMEO, value); |
614 | result = Timespan::TimeDiff(value)*1000; |
615 | #elif !defined(POCO_BROKEN_TIMEOUTS) |
616 | getOption(SOL_SOCKET, SO_SNDTIMEO, result); |
617 | #endif |
618 | if (_isBrokenTimeout) |
619 | result = _sndTimeout; |
620 | return result; |
621 | } |
622 | |
623 | |
624 | void SocketImpl::setReceiveTimeout(const Poco::Timespan& timeout) |
625 | { |
626 | #if defined(_WIN32) |
627 | int value = (int) timeout.totalMilliseconds(); |
628 | setOption(SOL_SOCKET, SO_RCVTIMEO, value); |
629 | #else |
630 | setOption(SOL_SOCKET, SO_RCVTIMEO, timeout); |
631 | #endif |
632 | _recvTimeout = timeout; |
633 | } |
634 | |
635 | |
636 | Poco::Timespan SocketImpl::getReceiveTimeout() |
637 | { |
638 | Timespan result; |
639 | #if defined(_WIN32) && !defined(POCO_BROKEN_TIMEOUTS) |
640 | int value; |
641 | getOption(SOL_SOCKET, SO_RCVTIMEO, value); |
642 | result = Timespan::TimeDiff(value)*1000; |
643 | #elif !defined(POCO_BROKEN_TIMEOUTS) |
644 | getOption(SOL_SOCKET, SO_RCVTIMEO, result); |
645 | #endif |
646 | if (_isBrokenTimeout) |
647 | result = _recvTimeout; |
648 | return result; |
649 | } |
650 | |
651 | |
652 | SocketAddress SocketImpl::address() |
653 | { |
654 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
655 | |
656 | char buffer[SocketAddress::MAX_ADDRESS_LENGTH]; |
657 | struct sockaddr* pSA = reinterpret_cast<struct sockaddr*>(buffer); |
658 | poco_socklen_t saLen = sizeof(buffer); |
659 | int rc = ::getsockname(_sockfd, pSA, &saLen); |
660 | if (rc == 0) |
661 | return SocketAddress(pSA, saLen); |
662 | else |
663 | error(); |
664 | return SocketAddress(); |
665 | } |
666 | |
667 | |
668 | SocketAddress SocketImpl::peerAddress() |
669 | { |
670 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
671 | |
672 | char buffer[SocketAddress::MAX_ADDRESS_LENGTH]; |
673 | struct sockaddr* pSA = reinterpret_cast<struct sockaddr*>(buffer); |
674 | poco_socklen_t saLen = sizeof(buffer); |
675 | int rc = ::getpeername(_sockfd, pSA, &saLen); |
676 | if (rc == 0) |
677 | return SocketAddress(pSA, saLen); |
678 | else |
679 | error(); |
680 | return SocketAddress(); |
681 | } |
682 | |
683 | |
684 | void SocketImpl::setOption(int level, int option, int value) |
685 | { |
686 | setRawOption(level, option, &value, sizeof(value)); |
687 | } |
688 | |
689 | |
690 | void SocketImpl::setOption(int level, int option, unsigned value) |
691 | { |
692 | setRawOption(level, option, &value, sizeof(value)); |
693 | } |
694 | |
695 | |
696 | void SocketImpl::setOption(int level, int option, unsigned char value) |
697 | { |
698 | setRawOption(level, option, &value, sizeof(value)); |
699 | } |
700 | |
701 | |
702 | void SocketImpl::setOption(int level, int option, const IPAddress& value) |
703 | { |
704 | setRawOption(level, option, value.addr(), value.length()); |
705 | } |
706 | |
707 | |
708 | void SocketImpl::setOption(int level, int option, const Poco::Timespan& value) |
709 | { |
710 | struct timeval tv; |
711 | tv.tv_sec = (long) value.totalSeconds(); |
712 | tv.tv_usec = (long) value.useconds(); |
713 | |
714 | setRawOption(level, option, &tv, sizeof(tv)); |
715 | } |
716 | |
717 | |
718 | void SocketImpl::setRawOption(int level, int option, const void* value, poco_socklen_t length) |
719 | { |
720 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
721 | |
722 | #if defined(POCO_VXWORKS) |
723 | int rc = ::setsockopt(_sockfd, level, option, (char*) value, length); |
724 | #else |
725 | int rc = ::setsockopt(_sockfd, level, option, reinterpret_cast<const char*>(value), length); |
726 | #endif |
727 | if (rc == -1) error(); |
728 | } |
729 | |
730 | |
731 | void SocketImpl::getOption(int level, int option, int& value) |
732 | { |
733 | poco_socklen_t len = sizeof(value); |
734 | getRawOption(level, option, &value, len); |
735 | } |
736 | |
737 | |
738 | void SocketImpl::getOption(int level, int option, unsigned& value) |
739 | { |
740 | poco_socklen_t len = sizeof(value); |
741 | getRawOption(level, option, &value, len); |
742 | } |
743 | |
744 | |
745 | void SocketImpl::getOption(int level, int option, unsigned char& value) |
746 | { |
747 | poco_socklen_t len = sizeof(value); |
748 | getRawOption(level, option, &value, len); |
749 | } |
750 | |
751 | |
752 | void SocketImpl::getOption(int level, int option, Poco::Timespan& value) |
753 | { |
754 | struct timeval tv; |
755 | poco_socklen_t len = sizeof(tv); |
756 | getRawOption(level, option, &tv, len); |
757 | value.assign(tv.tv_sec, tv.tv_usec); |
758 | } |
759 | |
760 | |
761 | void SocketImpl::getOption(int level, int option, IPAddress& value) |
762 | { |
763 | char buffer[IPAddress::MAX_ADDRESS_LENGTH]; |
764 | poco_socklen_t len = sizeof(buffer); |
765 | getRawOption(level, option, buffer, len); |
766 | value = IPAddress(buffer, len); |
767 | } |
768 | |
769 | |
770 | void SocketImpl::getRawOption(int level, int option, void* value, poco_socklen_t& length) |
771 | { |
772 | if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); |
773 | |
774 | int rc = ::getsockopt(_sockfd, level, option, reinterpret_cast<char*>(value), &length); |
775 | if (rc == -1) error(); |
776 | } |
777 | |
778 | |
779 | void SocketImpl::setLinger(bool on, int seconds) |
780 | { |
781 | struct linger l; |
782 | l.l_onoff = on ? 1 : 0; |
783 | l.l_linger = seconds; |
784 | setRawOption(SOL_SOCKET, SO_LINGER, &l, sizeof(l)); |
785 | } |
786 | |
787 | |
788 | void SocketImpl::getLinger(bool& on, int& seconds) |
789 | { |
790 | struct linger l; |
791 | poco_socklen_t len = sizeof(l); |
792 | getRawOption(SOL_SOCKET, SO_LINGER, &l, len); |
793 | on = l.l_onoff != 0; |
794 | seconds = l.l_linger; |
795 | } |
796 | |
797 | |
798 | void SocketImpl::setNoDelay(bool flag) |
799 | { |
800 | int value = flag ? 1 : 0; |
801 | setOption(IPPROTO_TCP, TCP_NODELAY, value); |
802 | } |
803 | |
804 | |
805 | bool SocketImpl::getNoDelay() |
806 | { |
807 | int value(0); |
808 | getOption(IPPROTO_TCP, TCP_NODELAY, value); |
809 | return value != 0; |
810 | } |
811 | |
812 | |
813 | void SocketImpl::setKeepAlive(bool flag) |
814 | { |
815 | int value = flag ? 1 : 0; |
816 | setOption(SOL_SOCKET, SO_KEEPALIVE, value); |
817 | } |
818 | |
819 | |
820 | bool SocketImpl::getKeepAlive() |
821 | { |
822 | int value(0); |
823 | getOption(SOL_SOCKET, SO_KEEPALIVE, value); |
824 | return value != 0; |
825 | } |
826 | |
827 | |
828 | void SocketImpl::setReuseAddress(bool flag) |
829 | { |
830 | int value = flag ? 1 : 0; |
831 | setOption(SOL_SOCKET, SO_REUSEADDR, value); |
832 | } |
833 | |
834 | |
835 | bool SocketImpl::getReuseAddress() |
836 | { |
837 | int value(0); |
838 | getOption(SOL_SOCKET, SO_REUSEADDR, value); |
839 | return value != 0; |
840 | } |
841 | |
842 | |
843 | void SocketImpl::setReusePort(bool flag) |
844 | { |
845 | #ifdef SO_REUSEPORT |
846 | try |
847 | { |
848 | int value = flag ? 1 : 0; |
849 | setOption(SOL_SOCKET, SO_REUSEPORT, value); |
850 | } |
851 | catch (IOException&) |
852 | { |
853 | // ignore error, since not all implementations |
854 | // support SO_REUSEPORT, even if the macro |
855 | // is defined. |
856 | } |
857 | #endif |
858 | } |
859 | |
860 | |
861 | bool SocketImpl::getReusePort() |
862 | { |
863 | #ifdef SO_REUSEPORT |
864 | int value(0); |
865 | getOption(SOL_SOCKET, SO_REUSEPORT, value); |
866 | return value != 0; |
867 | #else |
868 | return false; |
869 | #endif |
870 | } |
871 | |
872 | |
873 | void SocketImpl::setOOBInline(bool flag) |
874 | { |
875 | int value = flag ? 1 : 0; |
876 | setOption(SOL_SOCKET, SO_OOBINLINE, value); |
877 | } |
878 | |
879 | |
880 | bool SocketImpl::getOOBInline() |
881 | { |
882 | int value(0); |
883 | getOption(SOL_SOCKET, SO_OOBINLINE, value); |
884 | return value != 0; |
885 | } |
886 | |
887 | |
888 | void SocketImpl::setBroadcast(bool flag) |
889 | { |
890 | int value = flag ? 1 : 0; |
891 | setOption(SOL_SOCKET, SO_BROADCAST, value); |
892 | } |
893 | |
894 | |
895 | bool SocketImpl::getBroadcast() |
896 | { |
897 | int value(0); |
898 | getOption(SOL_SOCKET, SO_BROADCAST, value); |
899 | return value != 0; |
900 | } |
901 | |
902 | |
903 | void SocketImpl::setBlocking(bool flag) |
904 | { |
905 | #if !defined(POCO_OS_FAMILY_UNIX) |
906 | int arg = flag ? 0 : 1; |
907 | ioctl(FIONBIO, arg); |
908 | #else |
909 | int arg = fcntl(F_GETFL); |
910 | long flags = arg & ~O_NONBLOCK; |
911 | if (!flag) flags |= O_NONBLOCK; |
912 | (void) fcntl(F_SETFL, flags); |
913 | #endif |
914 | _blocking = flag; |
915 | } |
916 | |
917 | |
918 | int SocketImpl::socketError() |
919 | { |
920 | int result(0); |
921 | getOption(SOL_SOCKET, SO_ERROR, result); |
922 | return result; |
923 | } |
924 | |
925 | |
926 | void SocketImpl::init(int af) |
927 | { |
928 | initSocket(af, SOCK_STREAM); |
929 | } |
930 | |
931 | |
932 | void SocketImpl::initSocket(int af, int type, int proto) |
933 | { |
934 | poco_assert (_sockfd == POCO_INVALID_SOCKET); |
935 | |
936 | _sockfd = ::socket(af, type, proto); |
937 | if (_sockfd == POCO_INVALID_SOCKET) |
938 | error(); |
939 | |
940 | #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) |
941 | // SIGPIPE sends a signal that if unhandled (which is the default) |
942 | // will crash the process. This only happens on UNIX, and not Linux. |
943 | // |
944 | // In order to have POCO sockets behave the same across platforms, it is |
945 | // best to just ignore SIGPIPE all together. |
946 | setOption(SOL_SOCKET, SO_NOSIGPIPE, 1); |
947 | #endif |
948 | } |
949 | |
950 | |
951 | void SocketImpl::ioctl(poco_ioctl_request_t request, int& arg) |
952 | { |
953 | #if defined(_WIN32) |
954 | int rc = ioctlsocket(_sockfd, request, reinterpret_cast<u_long*>(&arg)); |
955 | #elif defined(POCO_VXWORKS) |
956 | int rc = ::ioctl(_sockfd, request, (int) &arg); |
957 | #else |
958 | int rc = ::ioctl(_sockfd, request, &arg); |
959 | #endif |
960 | if (rc != 0) error(); |
961 | } |
962 | |
963 | |
964 | void SocketImpl::ioctl(poco_ioctl_request_t request, void* arg) |
965 | { |
966 | #if defined(_WIN32) |
967 | int rc = ioctlsocket(_sockfd, request, reinterpret_cast<u_long*>(arg)); |
968 | #elif defined(POCO_VXWORKS) |
969 | int rc = ::ioctl(_sockfd, request, (int) arg); |
970 | #else |
971 | int rc = ::ioctl(_sockfd, request, arg); |
972 | #endif |
973 | if (rc != 0) error(); |
974 | } |
975 | |
976 | |
977 | #if defined(POCO_OS_FAMILY_UNIX) |
978 | int SocketImpl::fcntl(poco_fcntl_request_t request) |
979 | { |
980 | int rc = ::fcntl(_sockfd, request); |
981 | if (rc == -1) error(); |
982 | return rc; |
983 | } |
984 | |
985 | |
986 | int SocketImpl::fcntl(poco_fcntl_request_t request, long arg) |
987 | { |
988 | int rc = ::fcntl(_sockfd, request, arg); |
989 | if (rc == -1) error(); |
990 | return rc; |
991 | } |
992 | #endif |
993 | |
994 | |
995 | void SocketImpl::reset(poco_socket_t aSocket) |
996 | { |
997 | _sockfd = aSocket; |
998 | } |
999 | |
1000 | |
1001 | void SocketImpl::error() |
1002 | { |
1003 | int err = lastError(); |
1004 | std::string empty; |
1005 | error(err, empty); |
1006 | } |
1007 | |
1008 | |
1009 | void SocketImpl::error(const std::string& arg) |
1010 | { |
1011 | error(lastError(), arg); |
1012 | } |
1013 | |
1014 | |
1015 | void SocketImpl::error(int code) |
1016 | { |
1017 | std::string arg; |
1018 | error(code, arg); |
1019 | } |
1020 | |
1021 | |
1022 | void SocketImpl::error(int code, const std::string& arg) |
1023 | { |
1024 | switch (code) |
1025 | { |
1026 | case POCO_ENOERR: return; |
1027 | case POCO_ESYSNOTREADY: |
1028 | throw NetException("Net subsystem not ready" , code); |
1029 | case POCO_ENOTINIT: |
1030 | throw NetException("Net subsystem not initialized" , code); |
1031 | case POCO_EINTR: |
1032 | throw IOException("Interrupted" , code); |
1033 | case POCO_EACCES: |
1034 | throw IOException("Permission denied" , code); |
1035 | case POCO_EFAULT: |
1036 | throw IOException("Bad address" , code); |
1037 | case POCO_EINVAL: |
1038 | throw InvalidArgumentException(code); |
1039 | case POCO_EMFILE: |
1040 | throw IOException("Too many open files" , code); |
1041 | case POCO_EWOULDBLOCK: |
1042 | throw IOException("Operation would block" , code); |
1043 | case POCO_EINPROGRESS: |
1044 | throw IOException("Operation now in progress" , code); |
1045 | case POCO_EALREADY: |
1046 | throw IOException("Operation already in progress" , code); |
1047 | case POCO_ENOTSOCK: |
1048 | throw IOException("Socket operation attempted on non-socket" , code); |
1049 | case POCO_EDESTADDRREQ: |
1050 | throw NetException("Destination address required" , code); |
1051 | case POCO_EMSGSIZE: |
1052 | throw NetException("Message too long" , code); |
1053 | case POCO_EPROTOTYPE: |
1054 | throw NetException("Wrong protocol type" , code); |
1055 | case POCO_ENOPROTOOPT: |
1056 | throw NetException("Protocol not available" , code); |
1057 | case POCO_EPROTONOSUPPORT: |
1058 | throw NetException("Protocol not supported" , code); |
1059 | case POCO_ESOCKTNOSUPPORT: |
1060 | throw NetException("Socket type not supported" , code); |
1061 | case POCO_ENOTSUP: |
1062 | throw NetException("Operation not supported" , code); |
1063 | case POCO_EPFNOSUPPORT: |
1064 | throw NetException("Protocol family not supported" , code); |
1065 | case POCO_EAFNOSUPPORT: |
1066 | throw NetException("Address family not supported" , code); |
1067 | case POCO_EADDRINUSE: |
1068 | throw NetException("Address already in use" , arg, code); |
1069 | case POCO_EADDRNOTAVAIL: |
1070 | throw NetException("Cannot assign requested address" , arg, code); |
1071 | case POCO_ENETDOWN: |
1072 | throw NetException("Network is down" , code); |
1073 | case POCO_ENETUNREACH: |
1074 | throw NetException("Network is unreachable" , code); |
1075 | case POCO_ENETRESET: |
1076 | throw NetException("Network dropped connection on reset" , code); |
1077 | case POCO_ECONNABORTED: |
1078 | throw ConnectionAbortedException(code); |
1079 | case POCO_ECONNRESET: |
1080 | throw ConnectionResetException(code); |
1081 | case POCO_ENOBUFS: |
1082 | throw IOException("No buffer space available" , code); |
1083 | case POCO_EISCONN: |
1084 | throw NetException("Socket is already connected" , code); |
1085 | case POCO_ENOTCONN: |
1086 | throw NetException("Socket is not connected" , code); |
1087 | case POCO_ESHUTDOWN: |
1088 | throw NetException("Cannot send after socket shutdown" , code); |
1089 | case POCO_ETIMEDOUT: |
1090 | throw TimeoutException(code); |
1091 | case POCO_ECONNREFUSED: |
1092 | throw ConnectionRefusedException(arg, code); |
1093 | case POCO_EHOSTDOWN: |
1094 | throw NetException("Host is down" , arg, code); |
1095 | case POCO_EHOSTUNREACH: |
1096 | throw NetException("No route to host" , arg, code); |
1097 | #if defined(POCO_OS_FAMILY_UNIX) |
1098 | case EPIPE: |
1099 | throw IOException("Broken pipe" , code); |
1100 | case EBADF: |
1101 | throw IOException("Bad socket descriptor" , code); |
1102 | case ENOENT: |
1103 | throw IOException("Not found" , arg, code); |
1104 | #endif |
1105 | default: |
1106 | throw IOException(NumberFormatter::format(code), arg, code); |
1107 | } |
1108 | } |
1109 | |
1110 | |
1111 | } } // namespace Poco::Net |
1112 | |