2 * WINGs WMConnection function library
4 * Copyright (c) 1999 Dan Pascu
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * - decide if we want to support connections with external sockets, else
25 * clean up the structure of the unneeded members.
30 #include "../src/config.h"
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
45 #include <sys/signal.h>
51 /* Some older systems does not define this (linux libc5, maybe others too) */
61 /* Stuff for setting the sockets into non-blocking mode. */
62 /*#ifdef __POSIX_SOURCE
63 # define NONBLOCK_OPT O_NONBLOCK
65 # define NONBLOCK_OPT FNDELAY
68 #define NONBLOCK_OPT O_NONBLOCK
71 #define NETBUF_SIZE 4096
74 #define DEF_TIMEOUT 600 /* 600 seconds == 10 minutes */
81 static Bool SigInitialized
= False
;
85 typedef struct TimeoutData
{
92 typedef struct W_Connection
{
93 int sock
; /* the socket we speak through */
96 WMHandlerID
*read
; /* the input read handler */
97 WMHandlerID
*write
; /* the input write handler */
98 WMHandlerID
*exception
; /* the input exception handler */
101 ConnectionDelegate
*delegate
; /* client delegates */
102 void *clientData
; /* client data */
103 unsigned int uflags
; /* flags for the client */
108 TimeoutData sendTimeout
;
110 WMConnectionState state
;
126 clearOutputQueue(WMConnection
*cPtr
) /*FOLD00*/
132 for (i
=0; i
<WMGetBagItemCount(cPtr
->outputQueue
); i
++)
133 WMReleaseData(WMGetFromBag(cPtr
->outputQueue
, i
));
135 WMEmptyBag(cPtr
->outputQueue
);
140 sendTimeout(void *cdata
) /*FOLD00*/
142 WMConnection
*cPtr
= (WMConnection
*) cdata
;
143 TimeoutData
*tPtr
= &cPtr
->sendTimeout
;
145 tPtr
->handler
= NULL
;
146 if (cPtr
->handler
.write
) {
147 WMDeleteInputHandler(cPtr
->handler
.write
);
148 cPtr
->handler
.write
= NULL
;
150 if (WMGetBagItemCount(cPtr
->outputQueue
)>0) {
151 clearOutputQueue(cPtr
);
152 if (cPtr
->delegate
&& cPtr
->delegate
->didTimeout
)
153 (*cPtr
->delegate
->didTimeout
)(cPtr
->delegate
, cPtr
);
159 inputHandler(int fd
, int mask
, void *clientData
) /*FOLD00*/
161 WMConnection
*cPtr
= (WMConnection
*)clientData
;
163 if (cPtr
->state
==WCClosed
|| cPtr
->state
==WCDied
)
166 if ((mask
& WIWriteMask
)) {
167 if (cPtr
->state
== WCInProgress
) {
169 int len
= sizeof(result
);
171 if (getsockopt(cPtr
->sock
, SOL_SOCKET
, SO_ERROR
,
172 (void*)&result
, &len
) == 0 && result
!= 0) {
173 cPtr
->state
= WCFailed
;
174 WCErrorCode
= result
;
175 /* should call wsyserrorwithcode(result, ...) here? */
177 cPtr
->state
= WCConnected
;
180 if (cPtr
->handler
.write
) {
181 WMDeleteInputHandler(cPtr
->handler
.write
);
182 cPtr
->handler
.write
= NULL
;
185 if (cPtr
->delegate
&& cPtr
->delegate
->didInitialize
)
186 (*cPtr
->delegate
->didInitialize
)(cPtr
->delegate
, cPtr
);
188 if (cPtr
->state
== WCFailed
)
190 } else if (cPtr
->state
== WCConnected
) {
191 WMFlushConnection(cPtr
);
198 /* if the connection died, may get destroyed in the delegate, so retain */
201 if ((mask
& WIReadMask
) && cPtr
->delegate
->didReceiveInput
)
202 (*cPtr
->delegate
->didReceiveInput
)(cPtr
->delegate
, cPtr
);
204 if ((mask
& WIExceptMask
) && cPtr
->delegate
->didCatchException
)
205 (*cPtr
->delegate
->didCatchException
)(cPtr
->delegate
, cPtr
);
212 setSocketNonBlocking(int sock
, Bool flag
) /*FOLD00*/
217 state
= fcntl(sock
, F_GETFL
, 0);
220 wsyserror("Failed to get socket flags with fcntl.");
224 isNonBlock
= (state
& NONBLOCK_OPT
) != 0;
229 state
|= NONBLOCK_OPT
;
233 state
&= ~NONBLOCK_OPT
;
236 if (fcntl(sock
, F_SETFL
, state
) < 0) {
237 wsyserror("Failed to set socket flags with fcntl.");
246 setConnectionAddress(WMConnection
*cPtr
, struct sockaddr_in
*socketaddr
) /*FOLD00*/
248 wassertr(cPtr
->address
==NULL
);
250 cPtr
->address
= wstrdup(inet_ntoa(socketaddr
->sin_addr
));
251 cPtr
->service
= wmalloc(16);
252 sprintf(cPtr
->service
, "%hu", ntohs(socketaddr
->sin_port
));
253 cPtr
->protocol
= wstrdup("tcp");
257 static struct sockaddr_in
*
258 getSocketAddress(char* name
, char* service
, char* protocol
) /*FOLD00*/
260 static struct sockaddr_in socketaddr
;
263 if (!protocol
|| protocol
[0]=='\0')
266 memset(&socketaddr
, 0, sizeof(struct sockaddr_in
));
267 socketaddr
.sin_family
= AF_INET
;
270 * If we were given a hostname, we use any address for that host.
271 * Otherwise we expect the given name to be an address unless it is
272 * NULL (any address).
274 if (name
&& name
[0]!='\0') {
275 WMHost
*host
= WMGetHostWithName(name
);
278 return NULL
; /* name is not a hostname nor a number and dot adr */
280 name
= WMGetHostAddress(host
);
281 #ifndef HAVE_INET_ATON
282 if ((socketaddr
.sin_addr
.s_addr
= inet_addr(name
)) == INADDR_NONE
) {
284 if (inet_aton(name
, &socketaddr
.sin_addr
) == 0) {
291 socketaddr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
294 if (!service
|| service
[0]=='\0') {
295 socketaddr
.sin_port
= 0;
296 } else if ((sp
= getservbyname(service
, protocol
))==0) {
300 portNumber
= strtoul(service
, &endptr
, 10);
302 if (service
[0]!='\0' && *endptr
=='\0' && portNumber
<65536) {
303 socketaddr
.sin_port
= htons(portNumber
);
308 socketaddr
.sin_port
= sp
->s_port
;
316 createConnectionWithSocket(int sock
, Bool closeOnRelease
) /*FOLD00*/
319 struct sigaction sig_action
;
321 cPtr
= wmalloc(sizeof(WMConnection
));
323 memset(cPtr
, 0, sizeof(WMConnection
));
326 cPtr
->sendTimeout
.timeout
= DEF_TIMEOUT
;
327 cPtr
->sendTimeout
.handler
= NULL
;
328 cPtr
->closeOnRelease
= closeOnRelease
;
329 cPtr
->outputQueue
= WMCreateBag(16);
330 cPtr
->state
= WCNotConnected
;
332 /* ignore dead pipe */
333 if (!SigInitialized
) {
334 sig_action
.sa_handler
= SIG_IGN
;
335 sig_action
.sa_flags
= SA_RESTART
;
336 sigaction(SIGPIPE
, &sig_action
, NULL
);
337 SigInitialized
= True
;
346 WMCreateConnectionWithSocket(int sock
, Bool closeOnRelease
) /*FOLD00*/
349 struct sockaddr_in clientname
;
352 cPtr
= createConnectionWithSocket(sock
, closeOnRelease
);
353 cPtr
->wasNonBlocking
= WMIsConnectionNonBlocking(cPtr
);
354 cPtr
->isNonBlocking
= cPtr
->wasNonBlocking
;
356 /* some way to find out if it is connected, and binded. can't find
357 if it listens though!!!
360 size
= sizeof(clientname
);
361 n
= getpeername(sock
, (struct sockaddr
*) &clientname
, &size
);
363 /* Since we have a peer, it means we are connected */
364 cPtr
->state
= WCConnected
;
366 size
= sizeof(clientname
);
367 n
= getsockname(sock
, (struct sockaddr
*) &clientname
, &size
);
369 /* We don't have a peer, but we are binded to an address.
370 * Assume we are listening on it (we don't know that for sure!)
372 cPtr
->state
= WCListening
;
374 cPtr
->state
= WCNotConnected
;
384 * host is the name on which we want to listen for incoming connections,
385 * and it must be a name of this host, or NULL if we want to listen
386 * on any incoming address.
387 * service is either a service name as present in /etc/services, or the port
388 * number we want to listen on. If NULL, a random port between
389 * 1024 and 65535 will be assigned to us.
390 * protocol is one of "tcp" or "udp". If NULL, "tcp" will be used by default.
391 * currently only "tcp" is supported.
394 WMCreateConnectionAsServerAtAddress(char *host
, char *service
, char *protocol
) /*FOLD00*/
397 struct sockaddr_in
*socketaddr
;
400 if ((socketaddr
= getSocketAddress(host
, service
, protocol
)) == NULL
) {
402 wwarning("Bad address-service-protocol combination");
406 /* Create the actual socket */
407 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
410 wsyserror("Unable to create socket");
415 * Set socket options. We try to make the port reusable and have it
416 * close as fast as possible without waiting in unnecessary wait states
420 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
422 if (bind(sock
, (struct sockaddr
*)socketaddr
, sizeof(*socketaddr
)) < 0) {
424 wsyserror("Unable to bind to address '%s:%hu'",
425 inet_ntoa(socketaddr
->sin_addr
),
426 ntohs(socketaddr
->sin_port
));
431 if (listen(sock
, 10) < 0) {
433 wsyserror("Unable to listen on port '%hu'",
434 ntohs(socketaddr
->sin_port
));
439 /* Find out what is the address/service/protocol we get */
440 /* In case some of address/service/protocol were NULL */
441 size
= sizeof(*socketaddr
);
442 if (getsockname(sock
, (struct sockaddr
*)socketaddr
, &size
) < 0) {
444 wsyserror("Unable to get socket address");
449 cPtr
= createConnectionWithSocket(sock
, True
);
450 cPtr
->state
= WCListening
;
451 WMSetConnectionNonBlocking(cPtr
, True
);
453 setConnectionAddress(cPtr
, socketaddr
);
460 WMCreateConnectionToAddress(char *host
, char *service
, char *protocol
) /*FOLD00*/
463 struct sockaddr_in
*socketaddr
;
466 if (service
==NULL
|| service
[0]=='\0') {
468 wwarning("Bad argument - service is not specified");
472 if (host
==NULL
|| host
[0]=='\0')
475 if ((socketaddr
= getSocketAddress(host
, service
, protocol
)) == NULL
) {
477 wwarning("Bad address-service-protocol combination");
481 /* Create the actual socket */
482 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
485 wsyserror("Unable to create socket");
488 /* make socket blocking while we connect. */
489 setSocketNonBlocking(sock
, False
);
490 if (connect(sock
, (struct sockaddr
*)socketaddr
, sizeof(*socketaddr
)) < 0) {
492 wsyserror("Unable to make connection to address '%s:%hu'",
493 inet_ntoa(socketaddr
->sin_addr
),
494 ntohs(socketaddr
->sin_port
));
499 cPtr
= createConnectionWithSocket(sock
, True
);
500 cPtr
->state
= WCConnected
;
501 WMSetConnectionNonBlocking(cPtr
, True
);
502 setConnectionAddress(cPtr
, socketaddr
);
509 WMCreateConnectionToAddressAndNotify(char *host
, char *service
, char *protocol
) /*FOLD00*/
512 /*TimeoutData *tPtr;*/
513 struct sockaddr_in
*socketaddr
;
517 if (service
==NULL
|| service
[0]=='\0') {
519 wwarning("Bad argument - service is not specified");
523 if (host
==NULL
|| host
[0]=='\0')
526 if ((socketaddr
= getSocketAddress(host
, service
, protocol
)) == NULL
) {
528 wwarning("Bad address-service-protocol combination");
532 /* Create the actual socket */
533 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
536 wsyserror("Unable to create socket");
539 isNonBlocking
= setSocketNonBlocking(sock
, True
);
540 if (connect(sock
, (struct sockaddr
*)socketaddr
, sizeof(*socketaddr
)) < 0) {
541 if (errno
!=EINPROGRESS
) {
543 wsyserror("Unable to make connection to address '%s:%hu'",
544 inet_ntoa(socketaddr
->sin_addr
),
545 ntohs(socketaddr
->sin_port
));
551 cPtr
= createConnectionWithSocket(sock
, True
);
552 cPtr
->state
= WCInProgress
;
553 cPtr
->isNonBlocking
= isNonBlocking
;
555 /*tPtr = &cPtr->sendTimeout;
556 tPtr->handler = WMAddTimerHandler(tPtr->timeout*1000, connectTimeout, cPtr);
558 cPtr
->handler
.write
= WMAddInputHandler(cPtr
->sock
, WIWriteMask
,
561 setConnectionAddress(cPtr
, socketaddr
);
568 removeAllHandlers(WMConnection
*cPtr
) /*FOLD00*/
570 if (cPtr
->handler
.read
)
571 WMDeleteInputHandler(cPtr
->handler
.read
);
572 if (cPtr
->handler
.write
)
573 WMDeleteInputHandler(cPtr
->handler
.write
);
574 if (cPtr
->handler
.exception
)
575 WMDeleteInputHandler(cPtr
->handler
.exception
);
576 if (cPtr
->sendTimeout
.handler
)
577 WMDeleteTimerHandler(cPtr
->sendTimeout
.handler
);
579 cPtr
->handler
.read
= NULL
;
580 cPtr
->handler
.write
= NULL
;
581 cPtr
->handler
.exception
= NULL
;
582 cPtr
->sendTimeout
.handler
= NULL
;
587 WMDestroyConnection(WMConnection
*cPtr
) /*FOLD00*/
589 if (cPtr
->closeOnRelease
&& cPtr
->sock
>=0) {
590 shutdown(cPtr
->sock
, SHUT_RDWR
);
594 removeAllHandlers(cPtr
);
595 clearOutputQueue(cPtr
);
596 WMFreeBag(cPtr
->outputQueue
);
599 wfree(cPtr
->address
);
600 wfree(cPtr
->service
);
601 wfree(cPtr
->protocol
);
609 WMCloseConnection(WMConnection
*cPtr
) /*FOLD00*/
612 shutdown(cPtr
->sock
, SHUT_RDWR
);
617 removeAllHandlers(cPtr
);
618 clearOutputQueue(cPtr
);
620 cPtr
->state
= WCClosed
;
625 WMAcceptConnection(WMConnection
*listener
) /*FOLD00*/
627 struct sockaddr_in clientname
;
630 WMConnection
*newConnection
;
632 if (listener
->state
!=WCListening
) {
633 wwarning("Called 'WMAcceptConnection()' on a non-listening connection");
638 size
= sizeof(clientname
);
639 newSock
= accept(listener
->sock
, (struct sockaddr
*) &clientname
, &size
);
641 if (errno
!=EAGAIN
&& errno
!=EWOULDBLOCK
) {
643 wsyserror("Could not accept connection");
650 newConnection
= createConnectionWithSocket(newSock
, True
);
651 WMSetConnectionNonBlocking(newConnection
, True
);
652 newConnection
->state
= WCConnected
;
653 setConnectionAddress(newConnection
, &clientname
);
655 return newConnection
;
660 WMGetConnectionAddress(WMConnection
*cPtr
) /*FOLD00*/
662 return cPtr
->address
;
667 WMGetConnectionService(WMConnection
*cPtr
) /*FOLD00*/
669 return cPtr
->service
;
674 WMGetConnectionProtocol(WMConnection
*cPtr
)
676 return cPtr
->protocol
;
681 WMGetConnectionSocket(WMConnection
*cPtr
) /*FOLD00*/
688 WMGetConnectionState(WMConnection
*cPtr
) /*FOLD00*/
695 WMEnqueueConnectionData(WMConnection
*cPtr
, WMData
*data
) /*FOLD00*/
697 wassertrv(cPtr
->state
!=WCNotConnected
&& cPtr
->state
!=WCListening
, False
);
698 wassertrv(cPtr
->state
!=WCInProgress
&& cPtr
->state
!=WCFailed
, False
);
700 if (cPtr
->state
!=WCConnected
)
703 WMPutInBag(cPtr
->outputQueue
, WMRetainData(data
));
709 WMSendConnectionData(WMConnection
*cPtr
, WMData
*data
) /*FOLD00*/
711 int bytes
, pos
, len
, totalTransfer
;
712 TimeoutData
*tPtr
= &cPtr
->sendTimeout
;
713 const unsigned char *dataBytes
;
715 wassertrv(cPtr
->state
!=WCNotConnected
&& cPtr
->state
!=WCListening
, -1);
716 wassertrv(cPtr
->state
!=WCInProgress
&& cPtr
->state
!=WCFailed
, -1);
718 if (cPtr
->state
!=WCConnected
)
721 /* If we have no data just flush the queue, else try to send data */
722 if (data
&& WMGetDataLength(data
)>0) {
723 WMPutInBag(cPtr
->outputQueue
, WMRetainData(data
));
724 /* If there already was something in queue, and also a write input
725 * handler is established, it means we were unable to send, so
726 * return and let the write handler notify us when we can send.
728 if (WMGetBagItemCount(cPtr
->outputQueue
)>1 && cPtr
->handler
.write
)
734 while (WMGetBagItemCount(cPtr
->outputQueue
) > 0) {
735 data
= WMGetFromBag(cPtr
->outputQueue
, 0);
736 dataBytes
= (const unsigned char *)WMDataBytes(data
);
737 len
= WMGetDataLength(data
);
738 pos
= cPtr
->bufPos
; /* where we're left last time */
741 bytes
= write(cPtr
->sock
, dataBytes
+pos
, len
- pos
);
747 /* save the position where we're left and add a timeout */
749 if (!tPtr
->handler
) {
750 tPtr
->handler
= WMAddTimerHandler(tPtr
->timeout
*1000,
753 if (!cPtr
->handler
.write
) {
754 cPtr
->handler
.write
=
755 WMAddInputHandler(cPtr
->sock
, WIWriteMask
,
758 return totalTransfer
;
761 cPtr
->state
= WCDied
;
762 /*clearOutputQueue(cPtr);*/
763 removeAllHandlers(cPtr
);
764 if (cPtr
->delegate
&& cPtr
->delegate
->didDie
)
765 (*cPtr
->delegate
->didDie
)(cPtr
->delegate
, cPtr
);
770 totalTransfer
+= bytes
;
773 WMDeleteFromBag(cPtr
->outputQueue
, 0);
776 WMDeleteTimerHandler(tPtr
->handler
);
777 tPtr
->handler
= NULL
;
779 if (cPtr
->handler
.write
) {
780 WMDeleteInputHandler(cPtr
->handler
.write
);
781 cPtr
->handler
.write
= NULL
;
785 return totalTransfer
;
790 * WMGetConnectionAvailableData(connection):
792 * will return a WMData structure containing the available data on the
793 * specified connection. If connection is non-blocking (default) and no data
794 * is available when this function is called, an empty WMData is returned.
796 * If an error occurs while reading or the other side closed connection,
797 * it will return NULL.
798 * Also trying to read from an already died or closed connection is
799 * considered to be an error condition, and will return NULL.
802 WMGetConnectionAvailableData(WMConnection
*cPtr
) /*FOLD00*/
804 char buffer
[NETBUF_SIZE
];
808 wassertrv(cPtr
->state
!=WCNotConnected
&& cPtr
->state
!=WCListening
, NULL
);
809 wassertrv(cPtr
->state
!=WCInProgress
&& cPtr
->state
!=WCFailed
, NULL
);
811 if (cPtr
->state
!=WCConnected
)
817 nbytes
= read(cPtr
->sock
, buffer
, NETBUF_SIZE
);
823 aData
= WMCreateDataWithCapacity(0);
827 cPtr
->state
= WCDied
;
828 removeAllHandlers(cPtr
);
829 if (cPtr
->delegate
&& cPtr
->delegate
->didDie
)
830 (*cPtr
->delegate
->didDie
)(cPtr
->delegate
, cPtr
);
833 } else if (nbytes
==0) { /* the other side has closed connection */
834 cPtr
->state
= WCClosed
;
835 removeAllHandlers(cPtr
);
836 if (cPtr
->delegate
&& cPtr
->delegate
->didDie
)
837 (*cPtr
->delegate
->didDie
)(cPtr
->delegate
, cPtr
);
839 aData
= WMCreateDataWithBytes(buffer
, nbytes
);
847 WMSetConnectionDelegate(WMConnection
*cPtr
, ConnectionDelegate
*delegate
) /*FOLD00*/
849 wassertr(cPtr
->sock
>= 0);
850 /* Don't try to set the delegate multiple times */
851 wassertr(cPtr
->delegate
== NULL
);
853 cPtr
->delegate
= delegate
;
854 if (delegate
&& delegate
->didReceiveInput
&& !cPtr
->handler
.read
)
855 cPtr
->handler
.read
= WMAddInputHandler(cPtr
->sock
, WIReadMask
,
857 if (delegate
&& delegate
->didCatchException
&& !cPtr
->handler
.exception
)
858 cPtr
->handler
.exception
= WMAddInputHandler(cPtr
->sock
, WIExceptMask
,
865 WMIsConnectionNonBlocking(WMConnection
*cPtr
) /*FOLD00*/
870 state
= fcntl(cPtr
->sock
, F_GETFL
, 0);
873 wsyserror("Failed to get socket flags with fcntl.");
874 /* If we can't use fcntl on socket, this probably also means we could
875 * not use fcntl to set non-blocking mode, and since a socket defaults
876 * to blocking when created, return False as the best assumption */
880 return ((state
& NONBLOCK_OPT
)!=0);
882 return cPtr
->isNonBlocking
;
889 WMSetConnectionNonBlocking(WMConnection
*cPtr
, Bool flag
) /*FOLD00*/
894 if (cPtr
->isNonBlocking
== flag
)
897 if (setSocketNonBlocking(cPtr
->sock
, flag
)==True
)
898 cPtr
->isNonBlocking
= flag
;
903 WMGetConnectionClientData(WMConnection
*cPtr
) /*FOLD00*/
905 return cPtr
->clientData
;
910 WMSetConnectionClientData(WMConnection
*cPtr
, void *data
) /*FOLD00*/
912 cPtr
->clientData
= data
;
917 WMGetConnectionFlags(WMConnection
*cPtr
) /*FOLD00*/
924 WMSetConnectionFlags(WMConnection
*cPtr
, unsigned int flags
) /*FOLD00*/
926 cPtr
->uflags
= flags
;
931 WMSetConnectionSendTimeout(WMConnection
*cPtr
, unsigned int timeout
) /*FOLD00*/
934 timeout
= DEF_TIMEOUT
;
936 cPtr
->sendTimeout
.timeout
= timeout
;