Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / connection.c
Commit [+]AuthorDateLineData
9c59becb dan1999-12-14 01:07:24 +00001/*
2 * WINGs WMConnection function library
d4de3d0a dan2002-09-09 04:25:51 +00003 *
4153e2fd dan2003-01-16 23:30:45 +00004 * Copyright (c) 1999-2003 Dan Pascu
d4de3d0a dan2002-09-09 04:25:51 +00005 *
9c59becb dan1999-12-14 01:07:24 +00006 * 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.
10 *
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.
15 *
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.
19 */
20
9c59becb dan1999-12-14 01:07:24 +000021/*
22 * TODO:
23 * - decide if we want to support connections with external sockets, else
24 * clean up the structure of the unneeded members.
046403db dan2001-04-15 01:22:56 +000025 * - decide what to do with all wwarning() calls that are still there.
9c59becb dan1999-12-14 01:07:24 +000026 *
27 */
28
882b9a8e kojima2001-07-23 20:31:32 +000029#include "wconfig.h"
3f9b7858 dan1999-12-14 02:21:04 +000030
9c59becb dan1999-12-14 01:07:24 +000031#include <unistd.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <stdarg.h>
37#include <errno.h>
38#include <sys/socket.h>
39#include <netinet/in.h>
40#include <arpa/inet.h>
41#include <netdb.h>
42#include <signal.h>
43#ifdef __FreeBSD__
44#include <sys/signal.h>
45#endif
46
47#include "WINGs.h"
48
9c59becb dan1999-12-14 01:07:24 +000049/* Some older systems does not define this (linux libc5, maybe others too) */
50#ifndef SHUT_RDWR
51# define SHUT_RDWR 2
52#endif
53
badfd68a dan2000-04-17 21:52:14 +000054/* For SunOS */
9c59becb dan1999-12-14 01:07:24 +000055#ifndef SA_RESTART
56# define SA_RESTART 0
57#endif
58
badfd68a dan2000-04-17 21:52:14 +000059/* For Solaris */
60#ifndef INADDR_NONE
f3ed042f dan2000-04-18 18:09:35 +000061# define INADDR_NONE -1
cf3bb1fd dan2000-04-17 21:24:58 +000062#endif
63
9c59becb dan1999-12-14 01:07:24 +000064/* Stuff for setting the sockets into non-blocking mode. */
6830b057 dan2004-10-12 21:28:27 +000065/*
66#ifdef __POSIX_SOURCE
9c59becb dan1999-12-14 01:07:24 +000067# define NONBLOCK_OPT O_NONBLOCK
68#else
69# define NONBLOCK_OPT FNDELAY
6830b057 dan2004-10-12 21:28:27 +000070#endif
71*/
c952eb84 dan1999-12-14 01:21:56 +000072
73#define NONBLOCK_OPT O_NONBLOCK
9c59becb dan1999-12-14 01:07:24 +000074
9c59becb dan1999-12-14 01:07:24 +000075#define NETBUF_SIZE 4096
76
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020077#define DEF_TIMEOUT 600 /* 600 seconds == 10 minutes */
27838589 dan2000-04-27 04:26:15 +000078
9c59becb dan1999-12-14 01:07:24 +000079int WCErrorCode = 0;
80
9c59becb dan1999-12-14 01:07:24 +000081static Bool SigInitialized = False;
82
27838589 dan2000-04-27 04:26:15 +000083static unsigned int DefaultTimeout = DEF_TIMEOUT;
84static unsigned int OpenTimeout = DEF_TIMEOUT;
85
9c59becb dan1999-12-14 01:07:24 +000086typedef struct TimeoutData {
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020087 unsigned timeout;
88 WMHandlerID *handler;
9c59becb dan1999-12-14 01:07:24 +000089} TimeoutData;
90
9c59becb dan1999-12-14 01:07:24 +000091typedef struct W_Connection {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020092 int sock; /* the socket we speak through */
9c59becb dan1999-12-14 01:07:24 +000093
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020094 struct {
95 WMHandlerID *read; /* the input read handler */
96 WMHandlerID *write; /* the input write handler */
97 WMHandlerID *exception; /* the input exception handler */
98 } handler;
9c59becb dan1999-12-14 01:07:24 +000099
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200100 ConnectionDelegate *delegate; /* client delegates */
101 void *clientData; /* client data */
102 unsigned int uflags; /* flags for the client */
9c59becb dan1999-12-14 01:07:24 +0000103
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200104 WMArray *outputQueue;
105 unsigned bufPos;
9c59becb dan1999-12-14 01:07:24 +0000106
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200107 TimeoutData sendTimeout;
108 TimeoutData openTimeout;
9c59becb dan1999-12-14 01:07:24 +0000109
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200110 WMConnectionState state;
111 WMConnectionTimeoutState timeoutState;
9c59becb dan1999-12-14 01:07:24 +0000112
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200113 char *address;
114 char *service;
115 char *protocol;
9c59becb dan1999-12-14 01:07:24 +0000116
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200117 Bool closeOnRelease;
118 Bool shutdownOnClose;
119 Bool wasNonBlocking;
120 Bool isNonBlocking;
9c59becb dan1999-12-14 01:07:24 +0000121
122} W_Connection;
123
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200124static void clearOutputQueue(WMConnection * cPtr)
125{
126 cPtr->bufPos = 0;
127 WMEmptyArray(cPtr->outputQueue);
128}
129
130static void openTimeout(void *cdata)
131{
132 WMConnection *cPtr = (WMConnection *) cdata;
133
134 cPtr->openTimeout.handler = NULL;
135 if (cPtr->handler.write) {
136 WMDeleteInputHandler(cPtr->handler.write);
137 cPtr->handler.write = NULL;
138 }
139 if (cPtr->state != WCConnected) {
140 cPtr->state = WCTimedOut;
141 cPtr->timeoutState = WCTWhileOpening;
142 if (cPtr->delegate && cPtr->delegate->didTimeout) {
143 (*cPtr->delegate->didTimeout) (cPtr->delegate, cPtr);
144 } else {
145 WMCloseConnection(cPtr);
146 cPtr->state = WCTimedOut; /* the above set state to WCClosed */
147 }
148 }
149}
150
151static void sendTimeout(void *cdata)
152{
153 WMConnection *cPtr = (WMConnection *) cdata;
154
155 cPtr->sendTimeout.handler = NULL;
156 if (cPtr->handler.write) {
157 WMDeleteInputHandler(cPtr->handler.write);
158 cPtr->handler.write = NULL;
159 }
160 if (WMGetArrayItemCount(cPtr->outputQueue) > 0) {
161 clearOutputQueue(cPtr);
162 cPtr->state = WCTimedOut;
163 cPtr->timeoutState = WCTWhileSending;
164 /* // should we close it no matter what (after calling the didTimeout
165 // delegate)? -Dan */
166 if (cPtr->delegate && cPtr->delegate->didTimeout) {
167 (*cPtr->delegate->didTimeout) (cPtr->delegate, cPtr);
168 } else {
169 WMCloseConnection(cPtr);
170 cPtr->state = WCTimedOut; /* the above set state to WCClosed */
171 }
172 }
173}
174
175static void inputHandler(int fd, int mask, void *clientData)
176{
177 WMConnection *cPtr = (WMConnection *) clientData;
9c59becb dan1999-12-14 01:07:24 +0000178
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200179 if (cPtr->state == WCClosed || cPtr->state == WCDied)
180 return;
9c59becb dan1999-12-14 01:07:24 +0000181
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200182 if ((mask & WIWriteMask)) {
183 int result;
9c59becb dan1999-12-14 01:07:24 +0000184
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200185 if (cPtr->state == WCInProgress) {
186 Bool failed;
187 socklen_t len = sizeof(result);
9c59becb dan1999-12-14 01:07:24 +0000188
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200189 WCErrorCode = 0;
190 if (getsockopt(cPtr->sock, SOL_SOCKET, SO_ERROR,
191 (void *)&result, &len) == 0 && result != 0) {
192 cPtr->state = WCFailed;
193 WCErrorCode = result;
194 failed = True;
195 /* should call wsyserrorwithcode(result, ...) here? */
196 } else {
197 cPtr->state = WCConnected;
198 failed = False;
199 }
27838589 dan2000-04-27 04:26:15 +0000200
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200201 if (cPtr->handler.write) {
202 WMDeleteInputHandler(cPtr->handler.write);
203 cPtr->handler.write = NULL;
204 }
27838589 dan2000-04-27 04:26:15 +0000205
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200206 if (cPtr->openTimeout.handler) {
207 WMDeleteTimerHandler(cPtr->openTimeout.handler);
208 cPtr->openTimeout.handler = NULL;
209 }
9c59becb dan1999-12-14 01:07:24 +0000210
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200211 if (cPtr->delegate && cPtr->delegate->didInitialize)
212 (*cPtr->delegate->didInitialize) (cPtr->delegate, cPtr);
9c59becb dan1999-12-14 01:07:24 +0000213
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200214 /* we use failed and not cPtr->state here, because cPtr may be
215 * destroyed by the delegate called above if the connection failed
216 */
217 if (failed)
218 return;
219 } else if (cPtr->state == WCConnected) {
220 result = WMFlushConnection(cPtr);
221 if (result > 0 && cPtr->delegate && cPtr->delegate->canResumeSending) {
222 (*cPtr->delegate->canResumeSending) (cPtr->delegate, cPtr);
223 }
224 }
225 }
9c59becb dan1999-12-14 01:07:24 +0000226
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200227 if (!cPtr->delegate)
228 return;
9c59becb dan1999-12-14 01:07:24 +0000229
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200230 /* if the connection died, may get destroyed in the delegate, so retain */
231 wretain(cPtr);
232
233 if ((mask & WIReadMask) && cPtr->delegate->didReceiveInput)
234 (*cPtr->delegate->didReceiveInput) (cPtr->delegate, cPtr);
235
236 if ((mask & WIExceptMask) && cPtr->delegate->didCatchException)
237 (*cPtr->delegate->didCatchException) (cPtr->delegate, cPtr);
238
239 wrelease(cPtr);
9c59becb dan1999-12-14 01:07:24 +0000240}
241
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200242static Bool setSocketNonBlocking(int sock, Bool flag)
9c59becb dan1999-12-14 01:07:24 +0000243{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200244 int state;
245 Bool isNonBlock;
246
247 state = fcntl(sock, F_GETFL, 0);
248
249 if (state < 0) {
250 /* set WCErrorCode here? -Dan */
251 return False;
252 }
253
254 isNonBlock = (state & NONBLOCK_OPT) != 0;
255
256 if (flag) {
257 if (isNonBlock)
258 return True;
259 state |= NONBLOCK_OPT;
260 } else {
261 if (!isNonBlock)
262 return True;
263 state &= ~NONBLOCK_OPT;
264 }
265
266 if (fcntl(sock, F_SETFL, state) < 0) {
267 /* set WCErrorCode here? -Dan */
268 return False;
269 }
9c59becb dan1999-12-14 01:07:24 +0000270
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200271 return True;
9c59becb dan1999-12-14 01:07:24 +0000272}
273
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200274static void setConnectionAddress(WMConnection * cPtr, struct sockaddr_in *socketaddr)
275{
276 wassertr(cPtr->address == NULL);
277
278 cPtr->address = wstrdup(inet_ntoa(socketaddr->sin_addr));
279 cPtr->service = wmalloc(16);
280 sprintf(cPtr->service, "%hu", ntohs(socketaddr->sin_port));
281 cPtr->protocol = wstrdup("tcp");
282}
9c59becb dan1999-12-14 01:07:24 +0000283
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200284static struct sockaddr_in *getSocketAddress(char *name, char *service, char *protocol)
9c59becb dan1999-12-14 01:07:24 +0000285{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200286 static struct sockaddr_in socketaddr;
287 struct servent *sp;
9c59becb dan1999-12-14 01:07:24 +0000288
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200289 if (!protocol || protocol[0] == 0)
290 protocol = "tcp";
9c59becb dan1999-12-14 01:07:24 +0000291
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200292 memset(&socketaddr, 0, sizeof(struct sockaddr_in));
293 socketaddr.sin_family = AF_INET;
9c59becb dan1999-12-14 01:07:24 +0000294
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200295 /*
296 * If we were given a hostname, we use any address for that host.
297 * Otherwise we expect the given name to be an address unless it is
298 * NULL (any address).
299 */
300 if (name && name[0] != 0) {
301 WMHost *host = WMGetHostWithName(name);
9c59becb dan1999-12-14 01:07:24 +0000302
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200303 if (!host)
304 return NULL; /* name is not a hostname nor a number and dot adr */
9c59becb dan1999-12-14 01:07:24 +0000305
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200306 name = WMGetHostAddress(host);
3f9b7858 dan1999-12-14 02:21:04 +0000307#ifndef HAVE_INET_ATON
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200308 if ((socketaddr.sin_addr.s_addr = inet_addr(name)) == INADDR_NONE) {
3f9b7858 dan1999-12-14 02:21:04 +0000309#else
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200310 if (inet_aton(name, &socketaddr.sin_addr) == 0) {
3f9b7858 dan1999-12-14 02:21:04 +0000311#endif
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200312 WMReleaseHost(host);
313 return NULL;
314 }
315 WMReleaseHost(host);
316 } else {
317 socketaddr.sin_addr.s_addr = htonl(INADDR_ANY);
318 }
9c59becb dan1999-12-14 01:07:24 +0000319
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200320 if (!service || service[0] == 0) {
321 socketaddr.sin_port = 0;
322 } else if ((sp = getservbyname(service, protocol)) == 0) {
323 char *endptr;
324 unsigned portNumber;
c28d3d3f dan2001-07-23 23:57:12 +0000325
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200326 portNumber = strtoul(service, &endptr, 10);
9c59becb dan1999-12-14 01:07:24 +0000327
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200328 if (service[0] != 0 && *endptr == 0 && portNumber < 65536) {
329 socketaddr.sin_port = htons(portNumber);
330 } else {
331 return NULL;
332 }
333 } else {
334 socketaddr.sin_port = sp->s_port;
335 }
c28d3d3f dan2001-07-23 23:57:12 +0000336
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200337 return &socketaddr;
338}
339
340static void dummyHandler(int signum)
9c59becb dan1999-12-14 01:07:24 +0000341{
9c59becb dan1999-12-14 01:07:24 +0000342}
9d98d884 dan2000-11-19 23:50:38 +0000343
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200344static WMConnection *createConnectionWithSocket(int sock, Bool closeOnRelease)
345{
346 WMConnection *cPtr;
347 struct sigaction sig_action;
348
349 cPtr = wmalloc(sizeof(WMConnection));
350 wretain(cPtr);
351 memset(cPtr, 0, sizeof(WMConnection));
352
353 fcntl(sock, F_SETFD, FD_CLOEXEC); /* by default close on exec */
354
355 cPtr->sock = sock;
356 cPtr->openTimeout.timeout = OpenTimeout;
357 cPtr->openTimeout.handler = NULL;
358 cPtr->sendTimeout.timeout = DefaultTimeout;
359 cPtr->sendTimeout.handler = NULL;
360 cPtr->closeOnRelease = closeOnRelease;
361 cPtr->shutdownOnClose = 1;
362 cPtr->outputQueue = WMCreateArrayWithDestructor(16, (WMFreeDataProc *) WMReleaseData);
363 cPtr->state = WCNotConnected;
364 cPtr->timeoutState = WCTNone;
365
366 /* ignore dead pipe */
367 if (!SigInitialized) {
368 /* Because POSIX mandates that only signal with handlers are reset
369 * accross an exec*(), we do not want to propagate ignoring SIGPIPEs
370 * to children. Hence the dummy handler. Philippe Troin <phil@fifi.org>
371 */
372 sig_action.sa_handler = &dummyHandler;
373 sig_action.sa_flags = SA_RESTART;
374 sigaction(SIGPIPE, &sig_action, NULL);
375 SigInitialized = True;
376 }
377
378 return cPtr;
379}
9c59becb dan1999-12-14 01:07:24 +0000380
381#if 0
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200382WMConnection *WMCreateConnectionWithSocket(int sock, Bool closeOnRelease)
383{
384 WMConnection *cPtr;
385 struct sockaddr_in clientname;
386 int size;
387 int n;
388
389 cPtr = createConnectionWithSocket(sock, closeOnRelease);
390 cPtr->wasNonBlocking = WMIsConnectionNonBlocking(cPtr);
391 cPtr->isNonBlocking = cPtr->wasNonBlocking;
392
393 /* some way to find out if it is connected, and binded. can't find
394 if it listens though!!!
395 */
396
397 size = sizeof(clientname);
398 n = getpeername(sock, (struct sockaddr *)&clientname, &size);
399 if (n == 0) {
400 /* Since we have a peer, it means we are connected */
401 cPtr->state = WCConnected;
402 } else {
403 size = sizeof(clientname);
404 n = getsockname(sock, (struct sockaddr *)&clientname, &size);
405 if (n == 0) {
406 /* We don't have a peer, but we are binded to an address.
407 * Assume we are listening on it (we don't know that for sure!)
408 */
409 cPtr->state = WCListening;
410 } else {
411 cPtr->state = WCNotConnected;
412 }
413 }
414
415 return cPtr;
9c59becb dan1999-12-14 01:07:24 +0000416}
417#endif
9d98d884 dan2000-11-19 23:50:38 +0000418
9c59becb dan1999-12-14 01:07:24 +0000419/*
420 * host is the name on which we want to listen for incoming connections,
421 * and it must be a name of this host, or NULL if we want to listen
422 * on any incoming address.
423 * service is either a service name as present in /etc/services, or the port
424 * number we want to listen on. If NULL, a random port between
425 * 1024 and 65535 will be assigned to us.
426 * protocol is one of "tcp" or "udp". If NULL, "tcp" will be used by default.
427 * currently only "tcp" is supported.
428 */
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200429WMConnection *WMCreateConnectionAsServerAtAddress(char *host, char *service, char *protocol)
430{
431 WMConnection *cPtr;
432 struct sockaddr_in *socketaddr;
433 socklen_t size;
434 int sock, on;
435
436 WCErrorCode = 0;
437
438 if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
439 wwarning(_("Bad address-service-protocol combination"));
440 return NULL;
441 }
9c59becb dan1999-12-14 01:07:24 +0000442
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200443 /* Create the actual socket */
444 sock = socket(PF_INET, SOCK_STREAM, 0);
445 if (sock < 0) {
446 WCErrorCode = errno;
447 return NULL;
448 }
449
450 /*
451 * Set socket options. We try to make the port reusable and have it
452 * close as fast as possible without waiting in unnecessary wait states
453 * on close.
454 */
455 on = 1;
456 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
457
458 if (bind(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
459 WCErrorCode = errno;
460 close(sock);
461 return NULL;
462 }
463
464 if (listen(sock, 10) < 0) {
465 WCErrorCode = errno;
466 close(sock);
467 return NULL;
468 }
469
470 /* Find out what is the address/service/protocol we get */
471 /* In case some of address/service/protocol were NULL */
472 size = sizeof(*socketaddr);
473 if (getsockname(sock, (struct sockaddr *)socketaddr, &size) < 0) {
474 WCErrorCode = errno;
475 close(sock);
476 return NULL;
477 }
9c59becb dan1999-12-14 01:07:24 +0000478
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200479 cPtr = createConnectionWithSocket(sock, True);
480 cPtr->state = WCListening;
481 WMSetConnectionNonBlocking(cPtr, True);
482
483 setConnectionAddress(cPtr, socketaddr);
484
485 return cPtr;
486}
487
488WMConnection *WMCreateConnectionToAddress(char *host, char *service, char *protocol)
489{
490 WMConnection *cPtr;
491 struct sockaddr_in *socketaddr;
492 int sock;
493
494 WCErrorCode = 0;
495
496 wassertrv(service != NULL && service[0] != 0, NULL);
497
498 if (host == NULL || host[0] == 0)
499 host = "localhost";
500
501 if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
502 wwarning(_("Bad address-service-protocol combination"));
503 return NULL;
504 }
505
506 /* Create the actual socket */
507 sock = socket(PF_INET, SOCK_STREAM, 0);
508 if (sock < 0) {
509 WCErrorCode = errno;
510 return NULL;
511 }
512 /* make socket blocking while we connect. */
513 setSocketNonBlocking(sock, False);
514 if (connect(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
515 WCErrorCode = errno;
516 close(sock);
517 return NULL;
518 }
9c59becb dan1999-12-14 01:07:24 +0000519
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200520 cPtr = createConnectionWithSocket(sock, True);
521 cPtr->state = WCConnected;
522 WMSetConnectionNonBlocking(cPtr, True);
523 setConnectionAddress(cPtr, socketaddr);
524
525 return cPtr;
526}
527
528WMConnection *WMCreateConnectionToAddressAndNotify(char *host, char *service, char *protocol)
529{
530 WMConnection *cPtr;
531 struct sockaddr_in *socketaddr;
532 int sock;
533 Bool isNonBlocking;
9c59becb dan1999-12-14 01:07:24 +0000534
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200535 WCErrorCode = 0;
9c59becb dan1999-12-14 01:07:24 +0000536
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200537 wassertrv(service != NULL && service[0] != 0, NULL);
9c59becb dan1999-12-14 01:07:24 +0000538
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200539 if (host == NULL || host[0] == 0)
540 host = "localhost";
9c59becb dan1999-12-14 01:07:24 +0000541
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200542 if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
543 wwarning(_("Bad address-service-protocol combination"));
544 return NULL;
545 }
9c59becb dan1999-12-14 01:07:24 +0000546
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200547 /* Create the actual socket */
548 sock = socket(PF_INET, SOCK_STREAM, 0);
549 if (sock < 0) {
550 WCErrorCode = errno;
551 return NULL;
552 }
553 isNonBlocking = setSocketNonBlocking(sock, True);
554 if (connect(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
555 if (errno != EINPROGRESS) {
556 WCErrorCode = errno;
557 close(sock);
558 return NULL;
559 }
560 }
9c59becb dan1999-12-14 01:07:24 +0000561
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200562 cPtr = createConnectionWithSocket(sock, True);
563 cPtr->state = WCInProgress;
564 cPtr->isNonBlocking = isNonBlocking;
9c59becb dan1999-12-14 01:07:24 +0000565
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200566 cPtr->handler.write = WMAddInputHandler(cPtr->sock, WIWriteMask, inputHandler, cPtr);
9c59becb dan1999-12-14 01:07:24 +0000567
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200568 cPtr->openTimeout.handler = WMAddTimerHandler(cPtr->openTimeout.timeout * 1000, openTimeout, cPtr);
9c59becb dan1999-12-14 01:07:24 +0000569
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200570 setConnectionAddress(cPtr, socketaddr);
9c59becb dan1999-12-14 01:07:24 +0000571
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200572 return cPtr;
573}
9c59becb dan1999-12-14 01:07:24 +0000574
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200575static void removeAllHandlers(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000576{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200577 if (cPtr->handler.read)
578 WMDeleteInputHandler(cPtr->handler.read);
579 if (cPtr->handler.write)
580 WMDeleteInputHandler(cPtr->handler.write);
581 if (cPtr->handler.exception)
582 WMDeleteInputHandler(cPtr->handler.exception);
583 if (cPtr->openTimeout.handler)
584 WMDeleteTimerHandler(cPtr->openTimeout.handler);
585 if (cPtr->sendTimeout.handler)
586 WMDeleteTimerHandler(cPtr->sendTimeout.handler);
9c59becb dan1999-12-14 01:07:24 +0000587
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200588 cPtr->handler.read = NULL;
589 cPtr->handler.write = NULL;
590 cPtr->handler.exception = NULL;
591 cPtr->openTimeout.handler = NULL;
592 cPtr->sendTimeout.handler = NULL;
593}
9c59becb dan1999-12-14 01:07:24 +0000594
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200595void WMDestroyConnection(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000596{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200597 if (cPtr->closeOnRelease && cPtr->sock >= 0) {
598 if (cPtr->shutdownOnClose) {
599 shutdown(cPtr->sock, SHUT_RDWR);
600 }
601 close(cPtr->sock);
602 }
9c59becb dan1999-12-14 01:07:24 +0000603
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200604 removeAllHandlers(cPtr);
605 WMFreeArray(cPtr->outputQueue); /* will also free the items with the destructor */
606
607 if (cPtr->address) {
608 wfree(cPtr->address);
609 wfree(cPtr->service);
610 wfree(cPtr->protocol);
611 }
612
613 wrelease(cPtr);
614}
9c59becb dan1999-12-14 01:07:24 +0000615
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200616void WMCloseConnection(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000617{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200618 if (cPtr->sock >= 0) {
619 if (cPtr->shutdownOnClose) {
620 shutdown(cPtr->sock, SHUT_RDWR);
621 }
622 close(cPtr->sock);
623 cPtr->sock = -1;
624 }
625
626 removeAllHandlers(cPtr);
627 clearOutputQueue(cPtr);
628
629 cPtr->state = WCClosed;
9c59becb dan1999-12-14 01:07:24 +0000630}
631
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200632WMConnection *WMAcceptConnection(WMConnection * listener)
633{
634 struct sockaddr_in clientname;
635 socklen_t size;
636 int newSock;
637 WMConnection *newConnection;
638
639 WCErrorCode = 0;
640 wassertrv(listener && listener->state == WCListening, NULL);
641
642 size = sizeof(clientname);
643 newSock = accept(listener->sock, (struct sockaddr *)&clientname, &size);
644 if (newSock < 0) {
645 WCErrorCode = ((errno != EAGAIN && errno != EWOULDBLOCK) ? errno : 0);
646 return NULL;
647 }
648
649 newConnection = createConnectionWithSocket(newSock, True);
650 WMSetConnectionNonBlocking(newConnection, True);
651 newConnection->state = WCConnected;
652 setConnectionAddress(newConnection, &clientname);
9c59becb dan1999-12-14 01:07:24 +0000653
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200654 return newConnection;
655}
656
657char *WMGetConnectionAddress(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000658{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200659 return cPtr->address;
9c59becb dan1999-12-14 01:07:24 +0000660}
661
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200662char *WMGetConnectionService(WMConnection * cPtr)
663{
664 return cPtr->service;
665}
9c59becb dan1999-12-14 01:07:24 +0000666
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200667char *WMGetConnectionProtocol(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000668{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200669 return cPtr->protocol;
9c59becb dan1999-12-14 01:07:24 +0000670}
671
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200672int WMGetConnectionSocket(WMConnection * cPtr)
673{
674 return cPtr->sock;
675}
9c59becb dan1999-12-14 01:07:24 +0000676
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200677WMConnectionState WMGetConnectionState(WMConnection * cPtr)
27838589 dan2000-04-27 04:26:15 +0000678{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200679 return cPtr->state;
27838589 dan2000-04-27 04:26:15 +0000680}
681
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200682WMConnectionTimeoutState WMGetConnectionTimeoutState(WMConnection * cPtr)
683{
684 return cPtr->timeoutState;
685}
27838589 dan2000-04-27 04:26:15 +0000686
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200687Bool WMEnqueueConnectionData(WMConnection * cPtr, WMData * data)
9c59becb dan1999-12-14 01:07:24 +0000688{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200689 wassertrv(cPtr->state != WCNotConnected && cPtr->state != WCListening, False);
690 wassertrv(cPtr->state != WCInProgress && cPtr->state != WCFailed, False);
9c59becb dan1999-12-14 01:07:24 +0000691
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200692 if (cPtr->state != WCConnected)
693 return False;
9c59becb dan1999-12-14 01:07:24 +0000694
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200695 WMAddToArray(cPtr->outputQueue, WMRetainData(data));
696 return True;
9c59becb dan1999-12-14 01:07:24 +0000697}
698
d4de3d0a dan2002-09-09 04:25:51 +0000699/*
700 * Return value:
701 * -1 - not connected or connection died while sending
702 * 0 - couldn't send the data (or part of it). data is saved in a queue
703 * and will be sent when possible. after it is sent the canResumeSending
704 * callback will be called.
705 * 1 - data was succesfully sent
706 */
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200707int WMSendConnectionData(WMConnection * cPtr, WMData * data)
708{
709 int bytes, pos, len;
710 TimeoutData *tPtr = &cPtr->sendTimeout;
711 const unsigned char *dataBytes;
712
713 wassertrv(cPtr->state != WCNotConnected && cPtr->state != WCListening, -1);
714 wassertrv(cPtr->state != WCInProgress && cPtr->state != WCFailed, -1);
715
716 if (cPtr->state != WCConnected)
717 return -1;
718
719 /* If we have no data just flush the queue, else try to send data */
720 if (data && WMGetDataLength(data) > 0) {
721 WMAddToArray(cPtr->outputQueue, WMRetainData(data));
722 /* If there already was something in queue, and also a write input
723 * handler is established, it means we were unable to send, so
724 * return and let the write handler notify us when we can send.
725 */
726 if (WMGetArrayItemCount(cPtr->outputQueue) > 1 && cPtr->handler.write)
727 return 0;
728 }
729
730 while (WMGetArrayItemCount(cPtr->outputQueue) > 0) {
731 data = WMGetFromArray(cPtr->outputQueue, 0);
732 dataBytes = (const unsigned char *)WMDataBytes(data);
733 len = WMGetDataLength(data);
734 pos = cPtr->bufPos; /* where we're left last time */
735 while (pos < len) {
736 again:
737 bytes = write(cPtr->sock, dataBytes + pos, len - pos);
738 if (bytes < 0) {
739 switch (errno) {
740 case EINTR:
741 goto again;
742 case EWOULDBLOCK:
743 /* save the position where we're left and add a timeout */
744 cPtr->bufPos = pos;
745 if (!tPtr->handler) {
746 tPtr->handler = WMAddTimerHandler(tPtr->timeout * 1000,
747 sendTimeout, cPtr);
748 }
749 if (!cPtr->handler.write) {
750 cPtr->handler.write =
751 WMAddInputHandler(cPtr->sock, WIWriteMask, inputHandler, cPtr);
752 }
753 return 0;
754 default:
755 WCErrorCode = errno;
756 cPtr->state = WCDied;
757 removeAllHandlers(cPtr);
758 if (cPtr->delegate && cPtr->delegate->didDie)
759 (*cPtr->delegate->didDie) (cPtr->delegate, cPtr);
760 return -1;
761 }
762 }
763 pos += bytes;
764 }
765 WMDeleteFromArray(cPtr->outputQueue, 0);
766 cPtr->bufPos = 0;
767 if (tPtr->handler) {
768 WMDeleteTimerHandler(tPtr->handler);
769 tPtr->handler = NULL;
770 }
771 /*if (cPtr->handler.write) {
772 WMDeleteInputHandler(cPtr->handler.write);
773 cPtr->handler.write = NULL;
774 } */
775 }
776
777 if (cPtr->handler.write) {
778 WMDeleteInputHandler(cPtr->handler.write);
779 cPtr->handler.write = NULL;
780 }
781
782 return 1;
9c59becb dan1999-12-14 01:07:24 +0000783}
972620c7 dan2000-11-19 20:08:14 +0000784
9c59becb dan1999-12-14 01:07:24 +0000785/*
786 * WMGetConnectionAvailableData(connection):
787 *
788 * will return a WMData structure containing the available data on the
789 * specified connection. If connection is non-blocking (default) and no data
790 * is available when this function is called, an empty WMData is returned.
791 *
792 * If an error occurs while reading or the other side closed connection,
793 * it will return NULL.
794 * Also trying to read from an already died or closed connection is
795 * considered to be an error condition, and will return NULL.
796 */
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200797WMData *WMGetConnectionAvailableData(WMConnection * cPtr)
798{
799 char buffer[NETBUF_SIZE];
800 int nbytes;
801 WMData *aData;
802
803 wassertrv(cPtr->state != WCNotConnected && cPtr->state != WCListening, NULL);
804 wassertrv(cPtr->state != WCInProgress && cPtr->state != WCFailed, NULL);
805
806 if (cPtr->state != WCConnected)
807 return NULL;
808
809 aData = NULL;
810
811 again:
812 nbytes = read(cPtr->sock, buffer, NETBUF_SIZE);
813 if (nbytes < 0) {
814 switch (errno) {
815 case EINTR:
816 goto again;
817 case EWOULDBLOCK:
818 aData = WMCreateDataWithCapacity(0);
819 break;
820 default:
821 WCErrorCode = errno;
822 cPtr->state = WCDied;
823 removeAllHandlers(cPtr);
824 if (cPtr->delegate && cPtr->delegate->didDie)
825 (*cPtr->delegate->didDie) (cPtr->delegate, cPtr);
826 break;
827 }
828 } else if (nbytes == 0) { /* the other side has closed connection */
829 cPtr->state = WCClosed;
830 removeAllHandlers(cPtr);
831 if (cPtr->delegate && cPtr->delegate->didDie)
832 (*cPtr->delegate->didDie) (cPtr->delegate, cPtr);
833 } else {
834 aData = WMCreateDataWithBytes(buffer, nbytes);
835 }
836
837 return aData;
838}
839
840void WMSetConnectionDelegate(WMConnection * cPtr, ConnectionDelegate * delegate)
841{
842 wassertr(cPtr->sock >= 0);
843 /* Don't try to set the delegate multiple times */
844 wassertr(cPtr->delegate == NULL);
845
846 cPtr->delegate = delegate;
847 if (delegate && delegate->didReceiveInput && !cPtr->handler.read)
848 cPtr->handler.read = WMAddInputHandler(cPtr->sock, WIReadMask, inputHandler, cPtr);
849 if (delegate && delegate->didCatchException && !cPtr->handler.exception)
850 cPtr->handler.exception = WMAddInputHandler(cPtr->sock, WIExceptMask, inputHandler, cPtr);
9c59becb dan1999-12-14 01:07:24 +0000851}
852
9c59becb dan1999-12-14 01:07:24 +0000853#if 0
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200854Bool WMIsConnectionNonBlocking(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000855{
856#if 1
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200857 int state;
9c59becb dan1999-12-14 01:07:24 +0000858
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200859 state = fcntl(cPtr->sock, F_GETFL, 0);
9c59becb dan1999-12-14 01:07:24 +0000860
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200861 if (state < 0) {
862 /* If we can't use fcntl on socket, this probably also means we could
863 * not use fcntl to set non-blocking mode, and since a socket defaults
864 * to blocking when created, return False as the best assumption */
865 return False;
866 }
9c59becb dan1999-12-14 01:07:24 +0000867
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200868 return ((state & NONBLOCK_OPT) != 0);
9c59becb dan1999-12-14 01:07:24 +0000869#else
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200870 return cPtr->isNonBlocking;
9c59becb dan1999-12-14 01:07:24 +0000871#endif
872}
873#endif
874
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200875Bool WMSetConnectionNonBlocking(WMConnection * cPtr, Bool flag)
9c59becb dan1999-12-14 01:07:24 +0000876{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200877 wassertrv(cPtr != NULL && cPtr->sock >= 0, False);
9c59becb dan1999-12-14 01:07:24 +0000878
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200879 flag = ((flag == 0) ? 0 : 1);
e4a53ba7 dan2002-03-28 04:20:30 +0000880
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200881 if (cPtr->isNonBlocking == flag)
882 return True;
9c59becb dan1999-12-14 01:07:24 +0000883
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200884 if (setSocketNonBlocking(cPtr->sock, flag) == True) {
885 cPtr->isNonBlocking = flag;
886 return True;
887 }
9d98d884 dan2000-11-19 23:50:38 +0000888
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200889 return False;
9d98d884 dan2000-11-19 23:50:38 +0000890}
891
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200892Bool WMSetConnectionCloseOnExec(WMConnection * cPtr, Bool flag)
9d98d884 dan2000-11-19 23:50:38 +0000893{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200894 wassertrv(cPtr != NULL && cPtr->sock >= 0, False);
9d98d884 dan2000-11-19 23:50:38 +0000895
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200896 if (fcntl(cPtr->sock, F_SETFD, ((flag == 0) ? 0 : FD_CLOEXEC)) < 0) {
897 return False;
898 }
9d98d884 dan2000-11-19 23:50:38 +0000899
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200900 return True;
9c59becb dan1999-12-14 01:07:24 +0000901}
902
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200903void WMSetConnectionShutdownOnClose(WMConnection * cPtr, Bool flag)
d4de3d0a dan2002-09-09 04:25:51 +0000904{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200905 cPtr->shutdownOnClose = ((flag == 0) ? 0 : 1);
d4de3d0a dan2002-09-09 04:25:51 +0000906}
907
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200908void *WMGetConnectionClientData(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000909{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200910 return cPtr->clientData;
9c59becb dan1999-12-14 01:07:24 +0000911}
912
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200913void WMSetConnectionClientData(WMConnection * cPtr, void *data)
9c59becb dan1999-12-14 01:07:24 +0000914{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200915 cPtr->clientData = data;
9c59becb dan1999-12-14 01:07:24 +0000916}
917
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200918unsigned int WMGetConnectionFlags(WMConnection * cPtr)
9c59becb dan1999-12-14 01:07:24 +0000919{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200920 return cPtr->uflags;
9c59becb dan1999-12-14 01:07:24 +0000921}
922
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200923void WMSetConnectionFlags(WMConnection * cPtr, unsigned int flags)
9c59becb dan1999-12-14 01:07:24 +0000924{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200925 cPtr->uflags = flags;
9c59becb dan1999-12-14 01:07:24 +0000926}
927
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200928WMArray *WMGetConnectionUnsentData(WMConnection * cPtr)
972620c7 dan2000-11-19 20:08:14 +0000929{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200930 return cPtr->outputQueue;
972620c7 dan2000-11-19 20:08:14 +0000931}
932
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200933void WMSetConnectionDefaultTimeout(unsigned int timeout)
9c59becb dan1999-12-14 01:07:24 +0000934{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200935 if (timeout == 0) {
936 DefaultTimeout = DEF_TIMEOUT;
937 } else {
938 DefaultTimeout = timeout;
939 }
27838589 dan2000-04-27 04:26:15 +0000940}
941
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200942void WMSetConnectionOpenTimeout(unsigned int timeout)
27838589 dan2000-04-27 04:26:15 +0000943{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200944 if (timeout == 0) {
945 OpenTimeout = DefaultTimeout;
946 } else {
947 OpenTimeout = timeout;
948 }
27838589 dan2000-04-27 04:26:15 +0000949}
950
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200951void WMSetConnectionSendTimeout(WMConnection * cPtr, unsigned int timeout)
27838589 dan2000-04-27 04:26:15 +0000952{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200953 if (timeout == 0) {
954 cPtr->sendTimeout.timeout = DefaultTimeout;
955 } else {
956 cPtr->sendTimeout.timeout = timeout;
957 }
9c59becb dan1999-12-14 01:07:24 +0000958}