File: | src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/lib/abyss/src/socket_unix.c |
Location: | line 418, column 1 |
Description: | Potential leak of memory pointed to by 'peerName' |
1 | /*============================================================================= | |||
2 | socket_unix.c | |||
3 | =============================================================================== | |||
4 | This is the implementation of TChanSwitch and TChannel (and | |||
5 | obsolete TSocket) for a standard Unix (POSIX) | |||
6 | stream socket -- what you create with a socket() C library call. | |||
7 | =============================================================================*/ | |||
8 | ||||
9 | #include "xmlrpc_config.h" | |||
10 | ||||
11 | #include <stdlib.h> | |||
12 | #include <assert.h> | |||
13 | #include <sys/types.h> | |||
14 | #include <unistd.h> | |||
15 | #include <stdio.h> | |||
16 | #include <poll.h> | |||
17 | #include <string.h> | |||
18 | #include <sys/socket.h> | |||
19 | #include <sys/time.h> | |||
20 | #include <netinet/in.h> | |||
21 | #include <netinet/tcp.h> | |||
22 | #include <netdb.h> | |||
23 | #include <arpa/inet.h> | |||
24 | #include <errno(*__errno_location ()).h> | |||
25 | ||||
26 | #if HAVE_SYS_FILIO_H0 | |||
27 | #include <sys/filio.h> | |||
28 | #endif | |||
29 | ||||
30 | #include "c_util.h" | |||
31 | #include "int.h" | |||
32 | #include "xmlrpc-c/util_int.h" | |||
33 | #include "xmlrpc-c/string_int.h" | |||
34 | #include "mallocvar.h" | |||
35 | #include "trace.h" | |||
36 | #include "chanswitch.h" | |||
37 | #include "channel.h" | |||
38 | #include "socket.h" | |||
39 | #include "xmlrpc-c/abyss.h" | |||
40 | ||||
41 | #include "socket_unix.h" | |||
42 | ||||
43 | #define sane_close(_it)do {if (_it > -1) { close(_it) ; _it = -1; }} while (_it > -1) do {if (_it > -1) { close(_it) ; _it = -1; }} while (_it > -1) | |||
44 | ||||
45 | ||||
46 | typedef struct { | |||
47 | int interruptorFd; | |||
48 | int interrupteeFd; | |||
49 | int inuse; | |||
50 | } interruptPipe; | |||
51 | ||||
52 | ||||
53 | ||||
54 | static void | |||
55 | initInterruptPipe(interruptPipe * pipeP, | |||
56 | const char ** const errorP) { | |||
57 | ||||
58 | int pipeFd[2] = {-1, -1}; | |||
59 | int rc; | |||
60 | ||||
61 | rc = pipe(pipeFd); | |||
62 | ||||
63 | if (rc != 0) { | |||
64 | xmlrpc_asprintf(errorP, "Unable to create a pipe to use to interrupt " | |||
65 | "waits. pipe() failed with errno %d (%s)", | |||
66 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
67 | pipeP->inuse = 0; | |||
68 | } else { | |||
69 | *errorP = NULL((void*)0); | |||
70 | pipeP->interruptorFd = pipeFd[1]; | |||
71 | pipeP->interrupteeFd = pipeFd[0]; | |||
72 | pipeP->inuse = 1; | |||
73 | } | |||
74 | } | |||
75 | ||||
76 | ||||
77 | ||||
78 | static void | |||
79 | termInterruptPipe(interruptPipe *pipeP) { | |||
80 | if (pipeP->inuse) { | |||
81 | int x = 0; | |||
82 | write(pipeP->interruptorFd, &x, sizeof(x)); | |||
83 | usleep(500); | |||
84 | shutdown(pipeP->interrupteeFd, 2); | |||
85 | sane_close(pipeP->interruptorFd)do {if (pipeP->interruptorFd > -1) { close(pipeP->interruptorFd ) ; pipeP->interruptorFd = -1; }} while (pipeP->interruptorFd > -1); | |||
86 | sane_close(pipeP->interrupteeFd)do {if (pipeP->interrupteeFd > -1) { close(pipeP->interrupteeFd ) ; pipeP->interrupteeFd = -1; }} while (pipeP->interrupteeFd > -1); | |||
87 | } | |||
88 | } | |||
89 | ||||
90 | ||||
91 | ||||
92 | struct socketUnix { | |||
93 | /*---------------------------------------------------------------------------- | |||
94 | The properties/state of a TChanSwitch or TChannel unique to the | |||
95 | Unix variety. | |||
96 | -----------------------------------------------------------------------------*/ | |||
97 | int fd; | |||
98 | /* File descriptor of the POSIX socket (such as is created by | |||
99 | socket() in the C library) for the socket. | |||
100 | */ | |||
101 | bool userSuppliedFd; | |||
102 | /* The file descriptor and associated POSIX socket belong to the | |||
103 | user; we did not create it. | |||
104 | */ | |||
105 | interruptPipe interruptPipe; | |||
106 | }; | |||
107 | ||||
108 | ||||
109 | ||||
110 | static bool | |||
111 | connected(int const fd) { | |||
112 | /*---------------------------------------------------------------------------- | |||
113 | Return TRUE iff the socket on file descriptor 'fd' is in the connected | |||
114 | state. | |||
115 | ||||
116 | If 'fd' does not identify a stream socket or we are unable to determine | |||
117 | the state of the stream socket, the answer is "false". | |||
118 | -----------------------------------------------------------------------------*/ | |||
119 | bool connected; | |||
120 | struct sockaddr sockaddr; | |||
121 | socklen_t nameLen; | |||
122 | int rc; | |||
123 | ||||
124 | nameLen = sizeof(sockaddr); | |||
125 | ||||
126 | rc = getpeername(fd, &sockaddr, &nameLen); | |||
127 | ||||
128 | if (rc == 0) | |||
129 | connected = TRUE1; | |||
130 | else | |||
131 | connected = FALSE0; | |||
132 | ||||
133 | return connected; | |||
134 | } | |||
135 | ||||
136 | ||||
137 | ||||
138 | void | |||
139 | SocketUnixInit(const char ** const errorP) { | |||
140 | ||||
141 | *errorP = NULL((void*)0); | |||
142 | } | |||
143 | ||||
144 | ||||
145 | ||||
146 | void | |||
147 | SocketUnixTerm(void) { | |||
148 | ||||
149 | } | |||
150 | ||||
151 | ||||
152 | ||||
153 | /*============================================================================= | |||
154 | TChannel | |||
155 | =============================================================================*/ | |||
156 | ||||
157 | static void | |||
158 | channelDestroy(TChannel * const channelP) { | |||
159 | ||||
160 | struct socketUnix * const socketUnixP = channelP->implP; | |||
161 | ||||
162 | termInterruptPipe(&socketUnixP->interruptPipe); | |||
163 | ||||
164 | if (!socketUnixP->userSuppliedFd) | |||
165 | sane_close(socketUnixP->fd)do {if (socketUnixP->fd > -1) { close(socketUnixP->fd ) ; socketUnixP->fd = -1; }} while (socketUnixP->fd > -1); | |||
166 | ||||
167 | free(socketUnixP); | |||
168 | channelP->implP = 0; | |||
169 | } | |||
170 | ||||
171 | ||||
172 | ||||
173 | static ChannelWriteImpl channelWrite; | |||
174 | ||||
175 | static void | |||
176 | channelWrite(TChannel * const channelP, | |||
177 | const unsigned char * const buffer, | |||
178 | uint32_t const len, | |||
179 | bool * const failedP) { | |||
180 | ||||
181 | struct socketUnix * const socketUnixP = channelP->implP; | |||
182 | ||||
183 | size_t bytesLeft; | |||
184 | bool error; | |||
185 | int to_count = 0; | |||
186 | ||||
187 | assert(sizeof(size_t) >= sizeof(len))((sizeof(size_t) >= sizeof(len)) ? (void) (0) : __assert_fail ("sizeof(size_t) >= sizeof(len)", "../../../../libs/xmlrpc-c/lib/abyss/src/socket_unix.c" , 187, __PRETTY_FUNCTION__)); | |||
188 | ||||
189 | for (bytesLeft = len, error = FALSE0; bytesLeft > 0 && !error; ) { | |||
190 | size_t const maxSend = 4096 * 2; /* with respect to resource allocation this might be a better value than 2^31 */ | |||
191 | ssize_t rc = 0; | |||
192 | ||||
193 | rc = send(socketUnixP->fd, buffer + len - bytesLeft, MIN(maxSend, bytesLeft)((maxSend) < (bytesLeft) ? (maxSend) : (bytesLeft)), 0); | |||
194 | if (rc > 0) { /* 0 means connection closed; < 0 means severe error ; > 0 means bytes transferred */ | |||
195 | to_count = 0; | |||
196 | bytesLeft -= rc; | |||
197 | if (ChannelTraceIsActive) | |||
198 | fprintf(stderrstderr, "Abyss: sent %d bytes: '%.*s'\n", rc, MIN(rc, 4096)((rc) < (4096) ? (rc) : (4096)), buffer + len - bytesLeft); | |||
199 | } | |||
200 | else if (!rc) { | |||
201 | error = TRUE1; | |||
202 | if (ChannelTraceIsActive) | |||
203 | fprintf(stderrstderr, "\nAbyss: send() failed: socket closed"); | |||
204 | } | |||
205 | else { | |||
206 | error = TRUE1; | |||
207 | if (errno(*__errno_location ()) == EWOULDBLOCK11) { | |||
208 | usleep(20 * 1000); /* give socket another chance after xx millisec) */ | |||
209 | if (++to_count < 300) { | |||
210 | error = FALSE0; | |||
211 | } | |||
212 | if (ChannelTraceIsActive) | |||
213 | fprintf(stderrstderr, "\nAbyss: send() failed with errno %d (%s) cnt %d, will retry\n", errno(*__errno_location ()), strerror(errno(*__errno_location ())), to_count); | |||
214 | } | |||
215 | if (ChannelTraceIsActive) | |||
216 | fprintf(stderrstderr, "Abyss: send() failed with errno=%d (%s)", errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
217 | } | |||
218 | } | |||
219 | ||||
220 | *failedP = error; | |||
221 | ||||
222 | ||||
223 | } | |||
224 | ||||
225 | ||||
226 | ||||
227 | static ChannelReadImpl channelRead; | |||
228 | ||||
229 | static void | |||
230 | channelRead(TChannel * const channelP, | |||
231 | unsigned char * const buffer, | |||
232 | uint32_t const bufferSize, | |||
233 | uint32_t * const bytesReceivedP, | |||
234 | bool * const failedP) { | |||
235 | ||||
236 | struct socketUnix * const socketUnixP = channelP->implP; | |||
237 | int retries = 300; | |||
238 | ||||
239 | for (*failedP = TRUE1; *failedP && retries; retries--) { | |||
240 | int rc = recv(socketUnixP->fd, buffer, bufferSize, 0); | |||
241 | if (rc < 0) { | |||
242 | if (errno(*__errno_location ()) == EWOULDBLOCK11) { | |||
243 | if (ChannelTraceIsActive) | |||
244 | fprintf(stderrstderr, "\nAbyss: recv() failed with errno %d (%s) cnt %d, will retry\n", errno(*__errno_location ()), strerror(errno(*__errno_location ())), retries); | |||
245 | usleep(20 * 1000); /* give socket another chance after xx millisec)*/ | |||
246 | *failedP = FALSE0; | |||
247 | } else { | |||
248 | if (ChannelTraceIsActive) | |||
249 | fprintf(stderrstderr, "\nAbyss: recv() failed with errno %d (%s)\n", errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
250 | break; | |||
251 | } | |||
252 | } else { | |||
253 | *failedP = FALSE0; | |||
254 | *bytesReceivedP = rc; | |||
255 | ||||
256 | if (ChannelTraceIsActive) | |||
257 | fprintf(stderrstderr, "Abyss channel: read %u bytes: '%.*s'\n", bytesReceivedP, (int)(*bytesReceivedP), buffer); | |||
258 | } | |||
259 | } | |||
260 | } | |||
261 | ||||
262 | ||||
263 | ||||
264 | static ChannelWaitImpl channelWait; | |||
265 | ||||
266 | static void | |||
267 | channelWait(TChannel * const channelP, | |||
268 | bool const waitForRead, | |||
269 | bool const waitForWrite, | |||
270 | uint32_t const timeoutMs, | |||
271 | bool * const readyToReadP, | |||
272 | bool * const readyToWriteP, | |||
273 | bool * const failedP) { | |||
274 | /*---------------------------------------------------------------------------- | |||
275 | Wait for the channel to be immediately readable or writable. | |||
276 | ||||
277 | Readable means there is at least one byte of data to read or the | |||
278 | partner has disconnected. Writable means the channel will take at | |||
279 | least one byte of data to send or the partner has disconnected. | |||
280 | ||||
281 | 'waitForRead' and 'waitForWrite' determine which of these | |||
282 | conditions for which to wait; if both are true, we wait for either | |||
283 | one. | |||
284 | ||||
285 | We return before the requested condition holds if 'timeoutMs' | |||
286 | milliseconds pass. timeoutMs == TIME_INFINITE means infinity. | |||
287 | ||||
288 | We return before the requested condition holds if the process receives | |||
289 | (and catches) a signal, but only if it receives that signal a certain | |||
290 | time after we start running. (That means this function isn't useful | |||
291 | for most purposes). | |||
292 | ||||
293 | Return *readyToReadP == true if the reason for returning is that | |||
294 | the channel is immediately readable. *readyToWriteP is analogous | |||
295 | for writable. Both may be true. | |||
296 | ||||
297 | Return *failedP true iff we fail to wait for the requested | |||
298 | condition because of some unusual problem. Being interrupted by a | |||
299 | signal is not a failure. | |||
300 | ||||
301 | If one of these return value pointers is NULL, don't return that | |||
302 | value. | |||
303 | -----------------------------------------------------------------------------*/ | |||
304 | struct socketUnix * const socketUnixP = channelP->implP; | |||
305 | ||||
306 | /* Design note: some old systems may not have poll(). We're assuming | |||
307 | that we don't have to run on any such system. select() is more | |||
308 | universal, but can't handle a file descriptor with a high number. | |||
309 | ||||
310 | pselect() and ppoll() would allow us to be properly | |||
311 | interruptible by a signal -- we would add a signal mask to our | |||
312 | arguments. But ppoll() is fairly rare. pselect() is more | |||
313 | common, but in older Linux systems it doesn't actually work. | |||
314 | */ | |||
315 | bool readyToRead, readyToWrite, failed; | |||
316 | struct pollfd pollfds[2]; | |||
317 | int rc; | |||
318 | ||||
319 | pollfds[0].fd = socketUnixP->fd; | |||
320 | pollfds[0].events = | |||
321 | (waitForRead ? POLLIN0x001 : 0) | | |||
322 | (waitForWrite ? POLLOUT0x004 : 0); | |||
323 | ||||
324 | pollfds[1].fd = socketUnixP->interruptPipe.interrupteeFd; | |||
325 | pollfds[1].events = POLLIN0x001; | |||
326 | ||||
327 | rc = poll(pollfds, ARRAY_SIZE(pollfds)(sizeof(pollfds)/sizeof(pollfds[0])), | |||
328 | timeoutMs == TIME_INFINITE0xffffffff ? -1 : (int)timeoutMs); | |||
329 | ||||
330 | if (rc < 0) { | |||
331 | if (errno(*__errno_location ()) == EINTR4) { | |||
332 | failed = FALSE0; | |||
333 | readyToRead = FALSE0; | |||
334 | readyToWrite = FALSE0; | |||
335 | } else { | |||
336 | failed = TRUE1; | |||
337 | readyToRead = FALSE0; /* quiet compiler warning */ | |||
338 | readyToWrite = FALSE0; /* quiet compiler warning */ | |||
339 | } | |||
340 | } else { | |||
341 | failed = FALSE0; | |||
342 | readyToRead = !!(pollfds[0].revents & POLLIN0x001); | |||
343 | readyToWrite = !!(pollfds[0].revents & POLLOUT0x004); | |||
344 | } | |||
345 | ||||
346 | if (failedP) | |||
347 | *failedP = failed; | |||
348 | if (readyToReadP) | |||
349 | *readyToReadP = readyToRead; | |||
350 | if (readyToWriteP) | |||
351 | *readyToWriteP = readyToWrite; | |||
352 | } | |||
353 | ||||
354 | ||||
355 | ||||
356 | static ChannelInterruptImpl channelInterrupt; | |||
357 | ||||
358 | static void | |||
359 | channelInterrupt(TChannel * const channelP) { | |||
360 | /*---------------------------------------------------------------------------- | |||
361 | Interrupt any waiting that a thread might be doing in channelWait() | |||
362 | now or in the future. | |||
363 | ||||
364 | TODO: Make a way to reset this so that future channelWait()s can once | |||
365 | again wait. | |||
366 | -----------------------------------------------------------------------------*/ | |||
367 | struct socketUnix * const socketUnixP = channelP->implP; | |||
368 | unsigned char const zero[1] = {0u}; | |||
369 | ||||
370 | write(socketUnixP->interruptPipe.interruptorFd, &zero, sizeof(zero)); | |||
371 | } | |||
372 | ||||
373 | ||||
374 | ||||
375 | void | |||
376 | ChannelUnixGetPeerName(TChannel * const channelP, | |||
377 | struct sockaddr ** const sockaddrPP, | |||
378 | size_t * const sockaddrLenP, | |||
379 | const char ** const errorP) { | |||
380 | ||||
381 | struct socketUnix * const socketUnixP = channelP->implP; | |||
382 | ||||
383 | unsigned char * peerName; | |||
384 | socklen_t nameSize; | |||
385 | ||||
386 | nameSize = sizeof(struct sockaddr) + 1; | |||
387 | ||||
388 | peerName = malloc(nameSize); | |||
| ||||
389 | ||||
390 | if (peerName == NULL((void*)0)) | |||
391 | xmlrpc_asprintf(errorP, "Unable to allocate space for peer name"); | |||
392 | else { | |||
393 | int rc; | |||
394 | socklen_t nameLen; | |||
395 | nameLen = nameSize; /* initial value */ | |||
396 | rc = getpeername(socketUnixP->fd, (struct sockaddr *)peerName, | |||
397 | &nameLen); | |||
398 | ||||
399 | if (rc < 0) | |||
400 | xmlrpc_asprintf(errorP, "getpeername() failed. errno=%d (%s)", | |||
401 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
402 | else { | |||
403 | if (nameLen > nameSize-1) | |||
404 | xmlrpc_asprintf(errorP, | |||
405 | "getpeername() says the socket name is " | |||
406 | "larger than %u bytes, which means it is " | |||
407 | "not in the expected format.", | |||
408 | nameSize-1); | |||
409 | else { | |||
410 | *sockaddrPP = (struct sockaddr *)peerName; | |||
411 | *sockaddrLenP = nameLen; | |||
412 | *errorP = NULL((void*)0); | |||
413 | } | |||
414 | } | |||
415 | if (*errorP) | |||
416 | free(peerName); | |||
417 | } | |||
418 | } | |||
| ||||
419 | ||||
420 | ||||
421 | ||||
422 | static ChannelFormatPeerInfoImpl channelFormatPeerInfo; | |||
423 | ||||
424 | static void | |||
425 | channelFormatPeerInfo(TChannel * const channelP, | |||
426 | const char ** const peerStringP) { | |||
427 | ||||
428 | struct socketUnix * const socketUnixP = channelP->implP; | |||
429 | ||||
430 | struct sockaddr sockaddr; | |||
431 | socklen_t sockaddrLen; | |||
432 | int rc; | |||
433 | ||||
434 | sockaddrLen = sizeof(sockaddr); | |||
435 | ||||
436 | rc = getpeername(socketUnixP->fd, &sockaddr, &sockaddrLen); | |||
437 | ||||
438 | if (rc < 0) | |||
439 | xmlrpc_asprintf(peerStringP, "?? getpeername() failed. errno=%d (%s)", | |||
440 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
441 | else { | |||
442 | switch (sockaddr.sa_family) { | |||
443 | case AF_INET2: { | |||
444 | struct sockaddr_in * const sockaddrInP = | |||
445 | (struct sockaddr_in *) &sockaddr; | |||
446 | if (sockaddrLen < sizeof(*sockaddrInP)) | |||
447 | xmlrpc_asprintf(peerStringP, "??? getpeername() returned " | |||
448 | "the wrong size"); | |||
449 | else { | |||
450 | unsigned char * const ipaddr = (unsigned char *) | |||
451 | &sockaddrInP->sin_addr.s_addr; | |||
452 | xmlrpc_asprintf(peerStringP, "%u.%u.%u.%u:%hu", | |||
453 | ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3], | |||
454 | sockaddrInP->sin_port); | |||
455 | } | |||
456 | } break; | |||
457 | default: | |||
458 | xmlrpc_asprintf(peerStringP, "??? AF=%u", sockaddr.sa_family); | |||
459 | } | |||
460 | } | |||
461 | } | |||
462 | ||||
463 | ||||
464 | ||||
465 | static struct TChannelVtbl const channelVtbl = { | |||
466 | &channelDestroy, | |||
467 | &channelWrite, | |||
468 | &channelRead, | |||
469 | &channelWait, | |||
470 | &channelInterrupt, | |||
471 | &channelFormatPeerInfo, | |||
472 | }; | |||
473 | ||||
474 | ||||
475 | ||||
476 | static void | |||
477 | makeChannelInfo(struct abyss_unix_chaninfo ** const channelInfoPP, | |||
478 | struct sockaddr const peerAddr, | |||
479 | socklen_t const peerAddrLen, | |||
480 | const char ** const errorP) { | |||
481 | ||||
482 | struct abyss_unix_chaninfo * channelInfoP; | |||
483 | ||||
484 | MALLOCVAR(channelInfoP)channelInfoP = malloc(sizeof(*channelInfoP)); | |||
485 | ||||
486 | if (channelInfoP == NULL((void*)0)) | |||
487 | xmlrpc_asprintf(errorP, "Unable to allocate memory"); | |||
488 | else { | |||
489 | channelInfoP->peerAddrLen = peerAddrLen; | |||
490 | channelInfoP->peerAddr = peerAddr; | |||
491 | ||||
492 | *errorP = NULL((void*)0); | |||
493 | } | |||
494 | *channelInfoPP = channelInfoP; | |||
495 | } | |||
496 | ||||
497 | ||||
498 | ||||
499 | static void | |||
500 | makeChannelFromFd(int const fd, | |||
501 | TChannel ** const channelPP, | |||
502 | const char ** const errorP) { | |||
503 | ||||
504 | struct socketUnix * socketUnixP; | |||
505 | ||||
506 | MALLOCVAR(socketUnixP)socketUnixP = malloc(sizeof(*socketUnixP)); | |||
507 | ||||
508 | if (socketUnixP == NULL((void*)0)) | |||
509 | xmlrpc_asprintf(errorP, "Unable to allocate memory for Unix " | |||
510 | "channel descriptor"); | |||
511 | else { | |||
512 | TChannel * channelP; | |||
513 | ||||
514 | socketUnixP->fd = fd; | |||
515 | socketUnixP->userSuppliedFd = TRUE1; | |||
516 | ||||
517 | initInterruptPipe(&socketUnixP->interruptPipe, errorP); | |||
518 | ||||
519 | if (!*errorP) { | |||
520 | ChannelCreate(&channelVtbl, socketUnixP, &channelP); | |||
521 | ||||
522 | if (channelP == NULL((void*)0)) | |||
523 | xmlrpc_asprintf(errorP, "Unable to allocate memory for " | |||
524 | "channel descriptor."); | |||
525 | else { | |||
526 | *channelPP = channelP; | |||
527 | *errorP = NULL((void*)0); | |||
528 | } | |||
529 | if (*errorP) | |||
530 | termInterruptPipe(&socketUnixP->interruptPipe); | |||
531 | } | |||
532 | if (*errorP) | |||
533 | free(socketUnixP); | |||
534 | } | |||
535 | } | |||
536 | ||||
537 | ||||
538 | ||||
539 | void | |||
540 | ChannelUnixCreateFd(int const fd, | |||
541 | TChannel ** const channelPP, | |||
542 | struct abyss_unix_chaninfo ** const channelInfoPP, | |||
543 | const char ** const errorP) { | |||
544 | ||||
545 | struct sockaddr peerAddr; | |||
546 | socklen_t peerAddrLen; | |||
547 | int rc; | |||
548 | ||||
549 | peerAddrLen = sizeof(peerAddrLen); | |||
550 | ||||
551 | rc = getpeername(fd, &peerAddr, &peerAddrLen); | |||
552 | ||||
553 | if (rc != 0) { | |||
554 | if (errno(*__errno_location ()) == ENOTCONN107) | |||
555 | xmlrpc_asprintf(errorP, "Socket on file descriptor %d is not in " | |||
556 | "connected state.", fd); | |||
557 | else | |||
558 | xmlrpc_asprintf(errorP, "getpeername() failed on fd %d. " | |||
559 | "errno=%d (%s)", fd, errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
560 | } else { | |||
561 | makeChannelInfo(channelInfoPP, peerAddr, peerAddrLen, errorP); | |||
562 | if (!*errorP) { | |||
563 | makeChannelFromFd(fd, channelPP, errorP); | |||
564 | ||||
565 | if (*errorP) | |||
566 | free(*channelInfoPP); | |||
567 | } | |||
568 | } | |||
569 | } | |||
570 | ||||
571 | ||||
572 | ||||
573 | /*============================================================================= | |||
574 | TChanSwitch | |||
575 | =============================================================================*/ | |||
576 | ||||
577 | static SwitchDestroyImpl chanSwitchDestroy; | |||
578 | ||||
579 | static void | |||
580 | chanSwitchDestroy(TChanSwitch * const chanSwitchP) { | |||
581 | ||||
582 | struct socketUnix * const socketUnixP = chanSwitchP->implP; | |||
583 | ||||
584 | termInterruptPipe(&socketUnixP->interruptPipe); | |||
585 | ||||
586 | if (!socketUnixP->userSuppliedFd) | |||
587 | sane_close(socketUnixP->fd)do {if (socketUnixP->fd > -1) { close(socketUnixP->fd ) ; socketUnixP->fd = -1; }} while (socketUnixP->fd > -1); | |||
588 | ||||
589 | free(socketUnixP); | |||
590 | } | |||
591 | ||||
592 | ||||
593 | ||||
594 | static SwitchListenImpl chanSwitchListen; | |||
595 | ||||
596 | static void | |||
597 | chanSwitchListen(TChanSwitch * const chanSwitchP, | |||
598 | uint32_t const backlog, | |||
599 | const char ** const errorP) { | |||
600 | ||||
601 | struct socketUnix * const socketUnixP = chanSwitchP->implP; | |||
602 | ||||
603 | int32_t const minus1 = -1; | |||
604 | ||||
605 | int rc; | |||
606 | ||||
607 | /* Disable the Nagle algorithm to make persistant connections faster */ | |||
608 | ||||
609 | setsockopt(socketUnixP->fd, IPPROTO_TCPIPPROTO_TCP, TCP_NODELAY1, | |||
610 | &minus1, sizeof(minus1)); | |||
611 | ||||
612 | rc = listen(socketUnixP->fd, backlog); | |||
613 | ||||
614 | if (rc == -1) | |||
615 | xmlrpc_asprintf(errorP, "listen() failed with errno %d (%s)", | |||
616 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
617 | else | |||
618 | *errorP = NULL((void*)0); | |||
619 | } | |||
620 | ||||
621 | ||||
622 | ||||
623 | static void | |||
624 | waitForConnection(struct socketUnix * const listenSocketP, | |||
625 | bool * const interruptedP, | |||
626 | const char ** const errorP) { | |||
627 | /*---------------------------------------------------------------------------- | |||
628 | Wait for the listening socket to have a connection ready to accept. | |||
629 | ||||
630 | We return before the requested condition holds if the process receives | |||
631 | (and catches) a signal, but only if it receives that signal a certain | |||
632 | time after we start running. (That means this behavior isn't useful | |||
633 | for most purposes). | |||
634 | ||||
635 | We furthermore return before the requested condition holds if someone sends | |||
636 | a byte through the listening socket's interrupt pipe (or has sent one | |||
637 | previously since the most recent time the pipe was drained). | |||
638 | ||||
639 | Return *interruptedP == true if we return before there is a connection | |||
640 | ready to accept. | |||
641 | -----------------------------------------------------------------------------*/ | |||
642 | struct pollfd pollfds[2]; | |||
643 | int rc; | |||
644 | ||||
645 | pollfds[0].fd = listenSocketP->fd; | |||
646 | pollfds[0].events = POLLIN0x001; | |||
647 | ||||
648 | pollfds[1].fd = listenSocketP->interruptPipe.interrupteeFd; | |||
649 | pollfds[1].events = POLLIN0x001; | |||
650 | ||||
651 | rc = poll(pollfds, ARRAY_SIZE(pollfds)(sizeof(pollfds)/sizeof(pollfds[0])), -1); | |||
652 | ||||
653 | if (rc < 0) { | |||
654 | if (errno(*__errno_location ()) == EINTR4) { | |||
655 | *errorP = NULL((void*)0); | |||
656 | *interruptedP = TRUE1; | |||
657 | } else { | |||
658 | xmlrpc_asprintf(errorP, "poll() failed, errno = %d (%s)", | |||
659 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
660 | *interruptedP = FALSE0; /* quiet compiler warning */ | |||
661 | } | |||
662 | } else { | |||
663 | *errorP = NULL((void*)0); | |||
664 | *interruptedP = !(pollfds[0].revents & POLLIN0x001); | |||
665 | } | |||
666 | } | |||
667 | ||||
668 | ||||
669 | ||||
670 | static void | |||
671 | createChannelForAccept(int const acceptedFd, | |||
672 | struct sockaddr const peerAddr, | |||
673 | TChannel ** const channelPP, | |||
674 | void ** const channelInfoPP, | |||
675 | const char ** const errorP) { | |||
676 | /*---------------------------------------------------------------------------- | |||
677 | Make a channel object (TChannel) out of a socket just created by | |||
678 | accept() on a listening socket -- i.e. a socket for a client connection. | |||
679 | ||||
680 | 'acceptedFd' is the file descriptor of the socket. | |||
681 | ||||
682 | 'peerAddr' is the address of the client, from accept(). | |||
683 | -----------------------------------------------------------------------------*/ | |||
684 | struct abyss_unix_chaninfo * channelInfoP; | |||
685 | ||||
686 | makeChannelInfo(&channelInfoP, peerAddr, sizeof(peerAddr), errorP); | |||
687 | if (!*errorP) { | |||
688 | struct socketUnix * acceptedSocketP; | |||
689 | ||||
690 | MALLOCVAR(acceptedSocketP)acceptedSocketP = malloc(sizeof(*acceptedSocketP)); | |||
691 | ||||
692 | if (!acceptedSocketP) | |||
693 | xmlrpc_asprintf(errorP, "Unable to allocate memory"); | |||
694 | else { | |||
695 | acceptedSocketP->fd = acceptedFd; | |||
696 | acceptedSocketP->userSuppliedFd = FALSE0; | |||
697 | ||||
698 | initInterruptPipe(&acceptedSocketP->interruptPipe, errorP); | |||
699 | ||||
700 | if (!*errorP) { | |||
701 | TChannel * channelP; | |||
702 | ||||
703 | ChannelCreate(&channelVtbl, acceptedSocketP, &channelP); | |||
704 | if (!channelP) | |||
705 | xmlrpc_asprintf(errorP, | |||
706 | "Failed to create TChannel object."); | |||
707 | else { | |||
708 | *errorP = NULL((void*)0); | |||
709 | *channelPP = channelP; | |||
710 | *channelInfoPP = channelInfoP; | |||
711 | } | |||
712 | if (*errorP) | |||
713 | termInterruptPipe(&acceptedSocketP->interruptPipe); | |||
714 | } | |||
715 | if (*errorP) | |||
716 | free(acceptedSocketP); | |||
717 | } | |||
718 | if (*errorP) | |||
719 | free(channelInfoP); | |||
720 | } | |||
721 | } | |||
722 | ||||
723 | ||||
724 | ||||
725 | static SwitchAcceptImpl chanSwitchAccept; | |||
726 | ||||
727 | static void | |||
728 | chanSwitchAccept(TChanSwitch * const chanSwitchP, | |||
729 | TChannel ** const channelPP, | |||
730 | void ** const channelInfoPP, | |||
731 | const char ** const errorP) { | |||
732 | /*---------------------------------------------------------------------------- | |||
733 | Accept a connection via the channel switch *chanSwitchP. Return as | |||
734 | *channelPP the channel for the accepted connection. | |||
735 | ||||
736 | If no connection is waiting at *chanSwitchP, wait until one is. | |||
737 | ||||
738 | If we receive a signal while waiting, return immediately with | |||
739 | *channelPP == NULL. | |||
740 | -----------------------------------------------------------------------------*/ | |||
741 | struct socketUnix * const listenSocketP = chanSwitchP->implP; | |||
742 | ||||
743 | bool interrupted; | |||
744 | TChannel * channelP; | |||
745 | ||||
746 | interrupted = FALSE0; /* Haven't been interrupted yet */ | |||
747 | channelP = NULL((void*)0); /* No connection yet */ | |||
748 | *errorP = NULL((void*)0); /* No error yet */ | |||
749 | ||||
750 | while (!channelP && !*errorP && !interrupted) { | |||
751 | ||||
752 | waitForConnection(listenSocketP, &interrupted, errorP); | |||
753 | ||||
754 | if (!*errorP && !interrupted) { | |||
755 | struct sockaddr peerAddr; | |||
756 | socklen_t size = sizeof(peerAddr); | |||
757 | int rc; | |||
758 | ||||
759 | rc = accept(listenSocketP->fd, &peerAddr, &size); | |||
760 | ||||
761 | if (rc >= 0) { | |||
762 | int acceptedFd = rc; | |||
763 | ||||
764 | createChannelForAccept(acceptedFd, peerAddr, | |||
765 | &channelP, channelInfoPP, errorP); | |||
766 | ||||
767 | if (*errorP) | |||
768 | sane_close(acceptedFd)do {if (acceptedFd > -1) { close(acceptedFd) ; acceptedFd = -1; }} while (acceptedFd > -1); | |||
769 | } else if (errno(*__errno_location ()) == EINTR4) | |||
770 | interrupted = TRUE1; | |||
771 | else | |||
772 | xmlrpc_asprintf(errorP, "accept() failed, errno = %d (%s)", | |||
773 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
774 | } | |||
775 | } | |||
776 | *channelPP = channelP; | |||
777 | } | |||
778 | ||||
779 | ||||
780 | ||||
781 | static SwitchInterruptImpl chanSwitchInterrupt; | |||
782 | ||||
783 | static void | |||
784 | chanSwitchInterrupt(TChanSwitch * const chanSwitchP) { | |||
785 | /*---------------------------------------------------------------------------- | |||
786 | Interrupt any waiting that a thread might be doing in chanSwitchAccept() | |||
787 | now or in the future. | |||
788 | ||||
789 | TODO: Make a way to reset this so that future chanSwitchAccept()s can once | |||
790 | again wait. | |||
791 | -----------------------------------------------------------------------------*/ | |||
792 | struct socketUnix * const listenSocketP = chanSwitchP->implP; | |||
793 | ||||
794 | unsigned char const zero[1] = {0u}; | |||
795 | ||||
796 | write(listenSocketP->interruptPipe.interruptorFd, &zero, sizeof(zero)); | |||
797 | } | |||
798 | ||||
799 | ||||
800 | ||||
801 | static struct TChanSwitchVtbl const chanSwitchVtbl = { | |||
802 | &chanSwitchDestroy, | |||
803 | &chanSwitchListen, | |||
804 | &chanSwitchAccept, | |||
805 | &chanSwitchInterrupt, | |||
806 | }; | |||
807 | ||||
808 | ||||
809 | ||||
810 | static void | |||
811 | createChanSwitch(int const fd, | |||
812 | bool const userSuppliedFd, | |||
813 | TChanSwitch ** const chanSwitchPP, | |||
814 | const char ** const errorP) { | |||
815 | ||||
816 | struct socketUnix * socketUnixP; | |||
817 | ||||
818 | assert(!connected(fd))((!connected(fd)) ? (void) (0) : __assert_fail ("!connected(fd)" , "../../../../libs/xmlrpc-c/lib/abyss/src/socket_unix.c", 818 , __PRETTY_FUNCTION__)); | |||
819 | ||||
820 | MALLOCVAR(socketUnixP)socketUnixP = malloc(sizeof(*socketUnixP)); | |||
821 | ||||
822 | if (socketUnixP == NULL((void*)0)) | |||
823 | xmlrpc_asprintf(errorP, "unable to allocate memory for Unix " | |||
824 | "channel switch descriptor."); | |||
825 | else { | |||
826 | TChanSwitch * chanSwitchP; | |||
827 | ||||
828 | socketUnixP->fd = fd; | |||
829 | socketUnixP->userSuppliedFd = userSuppliedFd; | |||
830 | ||||
831 | initInterruptPipe(&socketUnixP->interruptPipe, errorP); | |||
832 | ||||
833 | if (!*errorP) { | |||
834 | ChanSwitchCreate(&chanSwitchVtbl, socketUnixP, &chanSwitchP); | |||
835 | if (*errorP) | |||
836 | termInterruptPipe(&socketUnixP->interruptPipe); | |||
837 | ||||
838 | if (chanSwitchP == NULL((void*)0)) | |||
839 | xmlrpc_asprintf(errorP, "Unable to allocate memory for " | |||
840 | "channel switch descriptor"); | |||
841 | else { | |||
842 | *chanSwitchPP = chanSwitchP; | |||
843 | *errorP = NULL((void*)0); | |||
844 | } | |||
845 | } | |||
846 | if (*errorP) | |||
847 | free(socketUnixP); | |||
848 | } | |||
849 | } | |||
850 | ||||
851 | ||||
852 | ||||
853 | static void | |||
854 | setSocketOptions(int const fd, | |||
855 | const char ** const errorP) { | |||
856 | ||||
857 | int32_t n = 1; | |||
858 | int rc; | |||
859 | ||||
860 | rc = setsockopt(fd, SOL_SOCKET1, SO_REUSEADDR2, (char*)&n, sizeof(n)); | |||
861 | ||||
862 | if (rc < 0) | |||
863 | xmlrpc_asprintf(errorP, "Failed to set socket options. " | |||
864 | "setsockopt() failed with errno %d (%s)", | |||
865 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
866 | else | |||
867 | *errorP = NULL((void*)0); | |||
868 | } | |||
869 | ||||
870 | ||||
871 | ||||
872 | static void | |||
873 | bindSocketToPort(int const fd, | |||
874 | struct in_addr * const addrP, | |||
875 | uint16_t const portNumber, | |||
876 | const char ** const errorP) { | |||
877 | ||||
878 | struct sockaddr_in name; | |||
879 | int rc; | |||
880 | int one = 1; | |||
881 | ||||
882 | name.sin_family = AF_INET2; | |||
883 | name.sin_port = htons(portNumber)(__extension__ ({ unsigned short int __v, __x = (unsigned short int) (portNumber); if (__builtin_constant_p (__x)) __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff ) << 8))); else __asm__ ("rorw $8, %w0" : "=r" (__v) : "0" (__x) : "cc"); __v; })); | |||
884 | if (addrP) | |||
885 | name.sin_addr = *addrP; | |||
886 | else | |||
887 | name.sin_addr.s_addr = INADDR_ANY((in_addr_t) 0x00000000); | |||
888 | ||||
889 | setsockopt(fd, SOL_SOCKET1, SO_REUSEADDR2, (void *)&one, sizeof(int)); | |||
890 | rc = bind(fd, (struct sockaddr *)&name, sizeof(name)); | |||
891 | ||||
892 | if (rc == -1) | |||
893 | xmlrpc_asprintf(errorP, "Unable to bind socket to port number %hu. " | |||
894 | "bind() failed with errno %d (%s)", | |||
895 | portNumber, errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
896 | else | |||
897 | *errorP = NULL((void*)0); | |||
898 | } | |||
899 | ||||
900 | ||||
901 | ||||
902 | void | |||
903 | ChanSwitchUnixCreate(unsigned short const portNumber, | |||
904 | TChanSwitch ** const chanSwitchPP, | |||
905 | const char ** const errorP) { | |||
906 | /*---------------------------------------------------------------------------- | |||
907 | Create a POSIX-socket-based channel switch. | |||
908 | ||||
909 | Use an IP socket. | |||
910 | ||||
911 | Set the socket's local address so that a subsequent "listen" will listen | |||
912 | on all IP addresses, port number 'portNumber'. | |||
913 | -----------------------------------------------------------------------------*/ | |||
914 | int rc; | |||
915 | rc = socket(AF_INET2, SOCK_STREAMSOCK_STREAM, 0); | |||
916 | if (rc < 0) | |||
917 | xmlrpc_asprintf(errorP, "socket() failed with errno %d (%s)", | |||
918 | errno(*__errno_location ()), strerror(errno(*__errno_location ()))); | |||
919 | else { | |||
920 | int socketFd = rc; | |||
921 | ||||
922 | setSocketOptions(socketFd, errorP); | |||
923 | if (!*errorP) { | |||
924 | bindSocketToPort(socketFd, NULL((void*)0), portNumber, errorP); | |||
925 | ||||
926 | if (!*errorP) { | |||
927 | bool const userSupplied = false; | |||
928 | createChanSwitch(socketFd, userSupplied, chanSwitchPP, errorP); | |||
929 | } | |||
930 | } | |||
931 | if (*errorP) | |||
932 | sane_close(socketFd)do {if (socketFd > -1) { close(socketFd) ; socketFd = -1; } } while (socketFd > -1); | |||
933 | } | |||
934 | } | |||
935 | ||||
936 | ||||
937 | ||||
938 | void | |||
939 | ChanSwitchUnixCreateFd(int const fd, | |||
940 | TChanSwitch ** const chanSwitchPP, | |||
941 | const char ** const errorP) { | |||
942 | ||||
943 | if (connected(fd)) | |||
944 | xmlrpc_asprintf(errorP, | |||
945 | "Socket (file descriptor %d) is in connected " | |||
946 | "state.", fd); | |||
947 | else { | |||
948 | bool const userSupplied = true; | |||
949 | createChanSwitch(fd, userSupplied, chanSwitchPP, errorP); | |||
950 | } | |||
951 | } | |||
952 | ||||
953 | ||||
954 | ||||
955 | /*============================================================================= | |||
956 | obsolete TSocket interface | |||
957 | =============================================================================*/ | |||
958 | ||||
959 | ||||
960 | void | |||
961 | SocketUnixCreateFd(int const fd, | |||
962 | TSocket ** const socketPP) { | |||
963 | ||||
964 | TSocket * socketP; | |||
965 | const char * error; | |||
966 | ||||
967 | if (connected(fd)) { | |||
968 | TChannel * channelP; | |||
969 | struct abyss_unix_chaninfo * channelInfoP; | |||
970 | ChannelUnixCreateFd(fd, &channelP, &channelInfoP, &error); | |||
971 | if (!error) | |||
972 | SocketCreateChannel(channelP, channelInfoP, &socketP); | |||
973 | } else { | |||
974 | TChanSwitch * chanSwitchP; | |||
975 | ChanSwitchUnixCreateFd(fd, &chanSwitchP, &error); | |||
976 | if (!error) | |||
977 | SocketCreateChanSwitch(chanSwitchP, &socketP); | |||
978 | } | |||
979 | if (error) { | |||
980 | *socketPP = NULL((void*)0); | |||
981 | xmlrpc_strfree(error); | |||
982 | } else | |||
983 | *socketPP = socketP; | |||
984 | } |