| File: | src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/lib/abyss/src/socket_unix.c |
| Location: | line 972, column 13 |
| Description: | Function call argument is an uninitialized value |
| 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 | } |