blob: 368485e227f058d3dab0372c0c7e41be440283f6 [file] [log] [blame]
Alexandre Lisionddd731e2014-01-31 11:50:08 -05001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <ucommon-config.h>
40#include <commoncpp/config.h>
41#include <commoncpp/export.h>
42#include <commoncpp/string.h>
43#include <commoncpp/socket.h>
44#include <commoncpp/dccp.h>
45#include <errno.h>
46
47#ifdef _MSWINDOWS_
48#include <io.h>
49#define _IOLEN64 (unsigned)
50#define _IORET64 (int)
51typedef int socklen_t;
52
53#define socket_errno WSAGetLastError()
54#else
55#include <sys/ioctl.h>
56#include <netinet/tcp.h>
57#ifdef HAVE_NET_IP6_H
58#include <netinet/ip6.h>
59#endif
60#define socket_errno errno
61# ifndef O_NONBLOCK
62# define O_NONBLOCK O_NDELAY
63# endif
64# ifdef IPPROTO_IP
65# ifndef SOL_IP
66# define SOL_IP IPPROTO_IP
67# endif // !SOL_IP
68# endif // IPPROTO_IP
69#endif // !WIN32
70
71#ifndef INADDR_LOOPBACK
72#define INADDR_LOOPBACK (unsigned long)0x7f000001
73#endif
74
75#ifdef HAVE_NETINET_IN_H
76#include <netinet/in.h>
77#endif
78
79#ifdef HAVE_SYS_FILIO_H
80#include <sys/filio.h>
81#endif
82
83#if defined(__hpux)
84#define _XOPEN_SOURCE_EXTENDED
85#endif
86
87#ifdef HAVE_NET_IF_H
88#include <net/if.h>
89#endif
90
91#ifndef _IOLEN64
92#define _IOLEN64
93#endif
94
95#ifndef _IORET64
96#define _IORET64
97#endif
98
99using namespace COMMONCPP_NAMESPACE;
100
101DCCPSocket::DCCPSocket(Family fam) :
102Socket(fam, SOCK_DCCP, IPPROTO_DCCP)
103{
104 family = fam;
105}
106
107DCCPSocket::DCCPSocket(DCCPSocket& server, timeout_t timeout) :
108Socket(accept(server.so, NULL, NULL))
109{
110 family = server.family;
111 Socket::state = CONNECTED;
112 socklen_t alen = sizeof(peer);
113
114 getpeername(so, (struct sockaddr *)&peer, &alen);
115
116 switch(family) {
117#ifdef CCXX_IPV6
118 case IPV6:
119 if(!server.onAccept(IPV6Host(peer.ipv6.sin6_addr), peer.ipv6.sin6_port))
120 endSocket();
121 break;
122#endif
123 case IPV4:
124 if(!server.onAccept(IPV4Host(peer.ipv4.sin_addr), peer.ipv4.sin_port))
125 endSocket();
126 break;
127 }
128}
129
130#ifdef HAVE_GETADDRINFO
131DCCPSocket::DCCPSocket(const char *name, Family fam, unsigned backlog) :
132Socket(fam, SOCK_DCCP, IPPROTO_DCCP)
133{
134 char namebuf[128], *cp;
135 struct addrinfo hint, *list = NULL, *first;
136 snprintf(namebuf, sizeof(namebuf), "%s", name);
137 cp = strrchr(namebuf, '/');
138 if(!cp)
139 cp = strrchr(namebuf, ':');
140
141 if(!cp) {
142 cp = namebuf;
143 name = NULL;
144 }
145 else {
146 name = namebuf;
147 *(cp++) = 0;
148 if(!strcmp(name, "*"))
149 name = NULL;
150 }
151
152 family = fam;
153 memset(&hint, 0, sizeof(hint));
154 hint.ai_family = family;
155 hint.ai_socktype = SOCK_DCCP;
156 hint.ai_protocol = IPPROTO_DCCP;
157 hint.ai_flags = AI_PASSIVE;
158
159 if(getaddrinfo(name, cp, &hint, &list) || !list) {
160 endSocket();
161 error(errBindingFailed, (char *)"Could not find service", errno);
162 return;
163 }
164
165#if defined(SO_REUSEADDR)
166 int opt = 1;
167 setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
168 (socklen_t)sizeof(opt));
169#endif
170
171 first = list;
172 while(list) {
173 if(!bind(so, list->ai_addr, (socklen_t)list->ai_addrlen)) {
174 state = BOUND;
175 break;
176 }
177 list = list->ai_next;
178 }
179 freeaddrinfo(first);
180 if(state != BOUND) {
181 endSocket();
182 error(errBindingFailed,(char *)"Could not bind socket",socket_errno);
183 return;
184 }
185
186 if(listen(so, backlog)) {
187 endSocket();
188 error(errBindingFailed,(char *)"Could not listen on socket",socket_errno);
189 return;
190 }
191}
192#else
193DCCPSocket::DCCPSocket(const char *name, Family fam, unsigned backlog) :
194Socket(fam, SOCK_DCCP, IPPROTO_DCCP)
195{
196 char namebuf[128], *cp;
197 struct sockaddr_in addr;
198#ifdef CCXX_IPV6
199 struct sockaddr_in6 addr6;
200#endif
201 struct sockaddr *ap = NULL;
202 socklen_t alen = 0;
203
204 struct servent *svc;
205
206 family = fam;
207 memset(&addr, 0, sizeof(addr));
208 snprintf(namebuf, sizeof(namebuf), "%s", name);
209 cp = strrchr(namebuf, '/');
210 if(!cp)
211 cp = strrchr(namebuf, ':');
212
213 if(!cp) {
214 cp = namebuf;
215 name = "*";
216 }
217 else {
218 name = namebuf;
219 *(cp++) = 0;
220 }
221
222 addr.sin_family = family;
223 if(isdigit(*cp))
224 addr.sin_port = htons(atoi(cp));
225 else {
226 mutex.enter();
227 svc = getservbyname(cp, "dccp");
228 if(svc)
229 addr.sin_port = svc->s_port;
230 mutex.leave();
231 if(!svc) {
232 endSocket();
233 error(errBindingFailed, "Could not find service", errno);
234 return;
235
236 }
237 }
238
239#if defined(SO_REUSEADDR)
240 int opt = 1;
241 setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&opt,
242 (socklen_t)sizeof(opt));
243#endif
244
245 switch(family) {
246#ifdef CCXX_IPV6
247 case IPV6: {
248 IPV6Address ia6(name);
249 addr6.sin6_port = addr.sin_port;
250 addr6.sin6_family = family;
251 addr6.sin6_addr = getaddress(ia6);
252 ap = (struct sockaddr *)&addr6;
253 alen = sizeof(addr6);
254 break;
255 }
256#endif
257 case IPV4:
258 IPV4Address ia(name);
259 addr.sin_addr = getaddress(ia);
260 ap = (struct sockaddr *)&addr;
261 alen = sizeof(addr);
262 break;
263 }
264
265 if(!ap || bind(so, (struct sockaddr *)ap, alen)) {
266 endSocket();
267 error(errBindingFailed,(char *)"Could not bind socket",socket_errno);
268 return;
269 }
270
271 if(listen(so, backlog)) {
272 endSocket();
273 error(errBindingFailed,(char *)"Could not listen on socket",
274 socket_errno);
275 return;
276 }
277 state = BOUND;
278}
279#endif
280
281DCCPSocket::DCCPSocket(const IPV4Address &ia, tpport_t port, unsigned backlog) :
282Socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)
283{
284 struct sockaddr_in addr;
285
286 memset(&addr, 0, sizeof(addr));
287 addr.sin_family = AF_INET;
288 addr.sin_addr = getaddress(ia);
289 addr.sin_port = htons(port);
290 family = IPV4;
291
292 memset(&peer, 0, sizeof(peer));
293 peer.ipv4 = addr;
294
295#if defined(SO_REUSEADDR)
296 int opt = 1;
297 setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, (socklen_t)sizeof(opt));
298#endif
299 if(bind(so, (struct sockaddr *)&addr, sizeof(addr))) {
300 endSocket();
301 error(errBindingFailed,(char *)"Could not bind socket",socket_errno);
302 return;
303 }
304
305 if(listen(so, backlog)) {
306 endSocket();
307 error(errBindingFailed,(char *)"Could not listen on socket",socket_errno);
308 return;
309 }
310 state = BOUND;
311}
312
313#ifdef CCXX_IPV6
314DCCPSocket::DCCPSocket(const IPV6Address &ia, tpport_t port, unsigned backlog) :
315Socket(AF_INET6, SOCK_DCCP, IPPROTO_DCCP)
316{
317 struct sockaddr_in6 addr;
318
319 memset(&addr, 0, sizeof(addr));
320 addr.sin6_family = AF_INET6;
321 addr.sin6_addr = getaddress(ia);
322 addr.sin6_port = htons(port);
323
324 family = IPV6;
325 memset(&peer, 0, sizeof(peer));
326 peer.ipv6 = addr;
327
328#if defined(SO_REUSEADDR)
329 int opt = 1;
330 setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, (socklen_t)sizeof(opt));
331#endif
332 if(bind(so, (struct sockaddr *)&addr, sizeof(addr))) {
333 endSocket();
334 error(errBindingFailed,(char *)"Could not bind socket",socket_errno);
335 return;
336 }
337
338 if(listen(so, backlog)) {
339 endSocket();
340 error(errBindingFailed,(char *)"Could not listen on socket",socket_errno);
341 return;
342 }
343 state = BOUND;
344}
345
346bool DCCPSocket::onAccept(const IPV6Host &ia, tpport_t port)
347{
348 return true;
349}
350
351#endif
352
353bool DCCPSocket::onAccept(const IPV4Host &ia, tpport_t port)
354{
355 return true;
356}
357
358IPV4Host DCCPSocket::getIPV4Sender(tpport_t *port) const
359{
360 if(port)
361 *port = ntohs(peer.ipv4.sin_port);
362 return IPV4Host(peer.ipv4.sin_addr);
363}
364
365#ifdef CCXX_IPV6
366IPV6Host DCCPSocket::getIPV6Sender(tpport_t *port) const
367{
368 return IPV6Host(peer.ipv6.sin6_addr);
369}
370#endif
371
372DCCPSocket::~DCCPSocket()
373{
374 endSocket();
375}
376
377void DCCPSocket::disconnect(void)
378{
379 if(Socket::state != CONNECTED)
380 return;
381
382 endSocket();
383 so = socket(family, SOCK_DCCP, IPPROTO_DCCP);
384 if(so != INVALID_SOCKET)
385 Socket::state = AVAILABLE;
386}
387
388#ifdef HAVE_GETADDRINFO
389void DCCPSocket::connect(const char *target)
390{
391 char namebuf[128];
392 char *cp;
393 struct addrinfo hint, *list = NULL, *next, *first;
394 bool connected = false;
395
396 snprintf(namebuf, sizeof(namebuf), "%s", target);
397 cp = strrchr(namebuf, '/');
398 if(!cp)
399 cp = strrchr(namebuf, ':');
400
401 if(!cp) {
402 connectError();
403 return;
404 }
405
406 *(cp++) = 0;
407
408 memset(&hint, 0, sizeof(hint));
409 hint.ai_family = family;
410 hint.ai_socktype = SOCK_DCCP;
411 hint.ai_protocol = IPPROTO_DCCP;
412
413 if(getaddrinfo(namebuf, cp, &hint, &list) || !list) {
414 connectError();
415 return;
416 }
417
418 first = list;
419
420 while(list) {
421 if(!::connect(so, list->ai_addr, (socklen_t)list->ai_addrlen)) {
422 connected = true;
423 break;
424 }
425 next = list->ai_next;
426 list = next;
427 }
428
429 freeaddrinfo(first);
430
431 if(!connected) {
432 connectError();
433 return;
434 }
435
436 Socket::state = CONNECTED;
437}
438#else
439void DCCPSocket::connect(const char *target)
440{
441 char namebuf[128];
442 char *cp;
443 struct servent *svc;
444 tpport_t port;
445
446 snprintf(namebuf, sizeof(namebuf), "%s", target);
447 cp = strrchr(namebuf, '/');
448 if(!cp)
449 cp = strrchr(namebuf, ':');
450
451 if(!cp) {
452 connectError();
453 return;
454 }
455
456 *(cp++) = 0;
457
458 if(isdigit(*cp))
459 port = atoi(cp);
460 else {
461 mutex.enter();
462 svc = getservbyname(cp, "dccp");
463 if(svc)
464 port = ntohs(svc->s_port);
465 mutex.leave();
466 if(!svc) {
467 connectError();
468 return;
469 }
470 }
471
472 switch(family) {
473 case IPV4:
474 connect(IPV4Host(namebuf), port);
475 break;
476#ifdef CCXX_IPV6
477 case IPV6:
478 connect(IPV6Host(namebuf), port);
479 break;
480#endif
481 default:
482 connectError();
483 }
484}
485#endif
486
487void DCCPSocket::connect(const IPV4Host &host, tpport_t port, timeout_t timeout)
488{
489 size_t i;
490 fd_set fds;
491 struct timeval to;
492 bool connected = false;
493 int rtn;
494 int sockopt;
495 socklen_t len = sizeof(sockopt);
496
497 for(i = 0 ; i < host.getAddressCount(); i++) {
498 struct sockaddr_in addr;
499 memset(&addr, 0, sizeof(addr));
500 addr.sin_family = AF_INET;
501 addr.sin_addr = host.getAddress(i);
502 addr.sin_port = htons(port);
503
504 if(timeout)
505 setCompletion(false);
506
507 // Win32 will crash if you try to connect to INADDR_ANY.
508 if ( INADDR_ANY == addr.sin_addr.s_addr )
509 addr.sin_addr.s_addr = INADDR_LOOPBACK;
510 rtn = ::connect(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr));
511 if(!rtn) {
512 connected = true;
513 break;
514 }
515
516#ifndef _MSWINDOWS_
517 if(errno == EINPROGRESS)
518#else
519 if(WSAGetLastError() == WSAEINPROGRESS)
520#endif
521 {
522 FD_ZERO(&fds);
523 FD_SET(so, &fds);
524 to.tv_sec = timeout / 1000;
525 to.tv_usec = timeout % 1000 * 1000;
526
527 // timeout check for connect completion
528
529 if(::select((int)so + 1, NULL, &fds, NULL, &to) < 1)
530 continue;
531
532 getsockopt(so, SOL_SOCKET, SO_ERROR, (char *)&sockopt, &len);
533 if(!sockopt) {
534 connected = true;
535 break;
536 }
537 endSocket();
538 so = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
539 if(so == INVALID_SOCKET)
540 break;
541 }
542 }
543
544 setCompletion(true);
545 if(!connected) {
546 rtn = errno;
547 errno = rtn;
548 connectError();
549 return;
550 }
551
552 Socket::state = CONNECTED;
553}
554
555#ifdef CCXX_IPV6
556void DCCPSocket::connect(const IPV6Host &host, tpport_t port, timeout_t timeout)
557{
558 size_t i;
559 fd_set fds;
560 struct timeval to;
561 bool connected = false;
562 int rtn;
563 int sockopt;
564 socklen_t len = sizeof(sockopt);
565
566 for(i = 0 ; i < host.getAddressCount(); i++) {
567 struct sockaddr_in6 addr;
568 memset(&addr, 0, sizeof(addr));
569 addr.sin6_family = AF_INET6;
570 addr.sin6_addr = host.getAddress(i);
571 addr.sin6_port = htons(port);
572
573 if(timeout)
574 setCompletion(false);
575
576 // Win32 will crash if you try to connect to INADDR_ANY.
577 if ( !memcmp(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)))
578 memcpy(&addr.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback));
579 rtn = ::connect(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr));
580 if(!rtn) {
581 connected = true;
582 break;
583 }
584
585#ifndef _MSWINDOWS_
586 if(errno == EINPROGRESS)
587#else
588 if(WSAGetLastError() == WSAEINPROGRESS)
589#endif
590 {
591 FD_ZERO(&fds);
592 FD_SET(so, &fds);
593 to.tv_sec = timeout / 1000;
594 to.tv_usec = timeout % 1000 * 1000;
595
596 // timeout check for connect completion
597
598 if(::select((int)so + 1, NULL, &fds, NULL, &to) < 1)
599 continue;
600
601 getsockopt(so, SOL_SOCKET, SO_ERROR, (char *)&sockopt, &len);
602 if(!sockopt) {
603 connected = true;
604 break;
605 }
606 endSocket();
607 so = socket(AF_INET6, SOCK_DCCP, IPPROTO_DCCP);
608 if(so == INVALID_SOCKET)
609 break;
610 }
611 }
612
613 setCompletion(true);
614 if(!connected) {
615 rtn = errno;
616 errno = rtn;
617 connectError();
618 return;
619 }
620
621 Socket::state = CONNECTED;
622}
623#endif
624
625bool DCCPSocket::setCCID(uint8_t ccid)
626{
627 uint8_t ccids[16]; /* for getting the available CCIDs, should be large enough */
628 socklen_t len = sizeof(ccids);
629 int ret;
630 bool ccid_supported = false;
631
632 /*
633 * Determine which CCIDs are available on the host
634 */
635 ret = getsockopt(so, SOL_DCCP, DCCP_SOCKOPT_AVAILABLE_CCIDS, (char *)&ccids, &len);
636 if (ret < 0) {
637 error(errInput,(char *)"Can not determine available CCIDs",socket_errno);
638 return false;
639 }
640
641 for (unsigned i = 0; i < sizeof(ccids); i++) {
642 if (ccid == ccids[i]) {
643 ccid_supported = true;
644 break;
645 }
646 }
647
648 if (!ccid_supported) {
649 error(errInput,(char *)"CCID specified is not supported",socket_errno);
650 return false;
651 }
652
653 if (setsockopt(so, SOL_DCCP, DCCP_SOCKOPT_CCID, (char *)&ccid, sizeof (ccid)) < 0) {
654 error(errInput,(char *)"Can not set CCID",socket_errno);
655 return false;
656 }
657
658 return true;
659}
660
661int DCCPSocket::getTxCCID()
662{
663 int ccid, ret;
664 socklen_t ccidlen;
665
666 ccidlen = sizeof(ccid);
667 ret = getsockopt(so, SOL_DCCP, DCCP_SOCKOPT_TX_CCID, (char *)&ccid, &ccidlen);
668 if (ret < 0) {
669 error(errInput,(char *)"Can not determine get current TX CCID value",socket_errno);
670 return -1;
671 }
672 return ccid;
673}
674
675int DCCPSocket::getRxCCID()
676{
677 int ccid, ret;
678 socklen_t ccidlen;
679
680 ccidlen = sizeof(ccid);
681 ret = getsockopt(so, SOL_DCCP, DCCP_SOCKOPT_RX_CCID, (char *)&ccid, &ccidlen);
682 if (ret < 0) {
683 error(errInput,(char *)"Can not determine get current DX CCID value",socket_errno);
684 return -1;
685 }
686 return ccid;
687}
688
689size_t DCCPSocket::available()
690{
691 size_t readsize;
692#ifndef _MSWINDOWS_
693 if (ioctl (so, FIONREAD, &readsize) < 0) {
694 error(errInput,(char *)"Error on retrieve the FIONREAD option.",socket_errno);
695 }
696#else
697 if (ioctlsocket(so, FIONREAD, (u_long *)&readsize)){
698 error(errInput,(char *)"Error on retrieve the FIONREAD option.",socket_errno);
699 }
700#endif
701 return readsize;
702}
703
704
705