/* * Copyright (C) 2008-2012 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "istgt.h" #include "istgt_log.h" #include "istgt_sock.h" #include "istgt_misc.h" //#define USE_POLLWAIT #undef USE_POLLWAIT #define TIMEOUT_RW 60 #define POLLWAIT 1000 #define PORTNUMLEN 32 #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif #if !defined(__GNUC__) #undef __attribute__ #define __attribute__(x) #endif int istgt_getaddr(int sock, char *saddr, int slen, char *caddr, int clen) { struct sockaddr_storage sa; socklen_t salen; int rc; memset(&sa, 0, sizeof sa); salen = sizeof sa; rc = getsockname(sock, (struct sockaddr *) &sa, &salen); if (rc != 0) { ISTGT_ERRLOG("getsockname() failed (errno=%d)\n", errno); return -1; } rc = getnameinfo((struct sockaddr *) &sa, salen, saddr, slen, NULL, 0, NI_NUMERICHOST); if (rc != 0) { ISTGT_ERRLOG("getnameinfo() failed (errno=%d)\n", errno); return -1; } memset(&sa, 0, sizeof sa); salen = sizeof sa; rc = getpeername(sock, (struct sockaddr *) &sa, &salen); if (rc != 0) { ISTGT_ERRLOG("getpeername() failed (errno=%d)\n", errno); return -1; } rc = getnameinfo((struct sockaddr *) &sa, salen, caddr, clen, NULL, 0, NI_NUMERICHOST); if (rc != 0) { ISTGT_ERRLOG("getnameinfo() failed (errno=%d)\n", errno); return -1; } return 0; } int istgt_listen(const char *ip, int port) { char buf[MAX_TMPBUF]; char portnum[PORTNUMLEN]; char *p; struct addrinfo hints, *res, *res0; int sock; int val = 1; int rc; if (ip == NULL) return -1; if (ip[0] == '[') { strlcpy(buf, ip + 1, sizeof buf); p = strchr(buf, ']'); if (p != NULL) *p = '\0'; ip = (const char *) &buf[0]; if (strcasecmp(ip, "*") == 0) { strlcpy(buf, "::", sizeof buf); ip = (const char *) &buf[0]; } } else { if (strcasecmp(ip, "*") == 0) { strlcpy(buf, "0.0.0.0", sizeof buf); ip = (const char *) &buf[0]; } } snprintf(portnum, sizeof portnum, "%d", port); memset(&hints, 0, sizeof hints); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; hints.ai_flags |= AI_PASSIVE; hints.ai_flags |= AI_NUMERICHOST; rc = getaddrinfo(ip, portnum, &hints, &res0); if (rc != 0) { ISTGT_ERRLOG("getaddrinfo() failed (errno=%d)\n", errno); return -1; } /* try listen */ sock = -1; for (res = res0; res != NULL; res = res->ai_next) { retry: sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) { /* error */ continue; } rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val); if (rc != 0) { /* error */ continue; } rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val); if (rc != 0) { /* error */ continue; } rc = bind(sock, res->ai_addr, res->ai_addrlen); if (rc == -1 && errno == EINTR) { /* interrupted? */ close(sock); sock = -1; goto retry; } if (rc != 0) { /* try next family */ close(sock); sock = -1; continue; } /* bind OK */ rc = listen(sock, 2); if (rc != 0) { close(sock); sock = -1; break; } break; } freeaddrinfo(res0); if (sock < 0) { return -1; } return sock; } int istgt_connect(const char *host, int port) { char buf[MAX_TMPBUF]; char portnum[PORTNUMLEN]; char *p; struct addrinfo hints, *res, *res0; int sock; int val = 1; int rc; if (host == NULL) return -1; if (host[0] == '[') { strlcpy(buf, host + 1, sizeof buf); p = strchr(buf, ']'); if (p != NULL) *p = '\0'; host = (const char *) &buf[0]; if (strcasecmp(host, "*") == 0) { strlcpy(buf, "::", sizeof buf); host = (const char *) &buf[0]; } } else { if (strcasecmp(host, "*") == 0) { strlcpy(buf, "0.0.0.0", sizeof buf); host = (const char *) &buf[0]; } } snprintf(portnum, sizeof portnum, "%d", port); memset(&hints, 0, sizeof hints); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; rc = getaddrinfo(host, portnum, &hints, &res0); if (rc != 0) { ISTGT_ERRLOG("getaddrinfo() failed (errno=%d)\n", errno); return -1; } /* try connect */ sock = -1; for (res = res0; res != NULL; res = res->ai_next) { retry: sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) { /* error */ continue; } rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val); if (rc != 0) { /* error */ continue; } rc = connect(sock, res->ai_addr, res->ai_addrlen); if (rc == -1 && errno == EINTR) { /* interrupted? */ close(sock); sock = -1; goto retry; } if (rc != 0) { /* try next family */ close(sock); sock = -1; continue; } /* connect OK */ break; } freeaddrinfo(res0); if (sock < 0) { return -1; } return sock; } int istgt_set_recvtimeout(int s, int msec) { struct timeval tv; int rc; memset(&tv, 0, sizeof tv); tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; rc = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); if (rc != 0) return -1; return 0; } int istgt_set_sendtimeout(int s, int msec) { struct timeval tv; int rc; memset(&tv, 0, sizeof tv); tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; rc = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); if (rc != 0) return -1; return 0; } int istgt_set_recvlowat(int s, int nbytes) { int val; int rc; val = nbytes; rc = setsockopt(s, SOL_SOCKET, SO_RCVLOWAT, &val, sizeof val); if (rc != 0) return -1; return 0; } #ifdef USE_POLLWAIT static int can_read_socket(int s, int msec) { struct pollfd fds[1]; int rc; fds[0].fd = s; fds[0].events = POLLIN; retry: do { rc = poll(fds, 1, msec); } while (rc == -1 && errno == EINTR); if (rc == -1 && errno == EAGAIN) { goto retry; } if (rc < 0) { /* error */ return -1; } if (fds[0].revents & POLLIN) { /* read OK */ return 1; } return 0; } static int can_write_socket(int s, int msec) { struct pollfd fds[1]; int rc; fds[0].fd = s; fds[0].events = POLLOUT; retry: do { rc = poll(fds, 1, msec); } while (rc == -1 && errno == EINTR); if (rc == -1 && errno == EAGAIN) { goto retry; } if (rc < 0) { /* error */ return -1; } if (fds[0].revents & POLLOUT) { /* write OK */ return 1; } return 0; } #endif /* USE_POLLWAIT */ #ifdef USE_POLLWAIT #define UNUSED_POLLWAIT(x) x #else #define UNUSED_POLLWAIT(x) x __attribute__((__unused__)) #endif ssize_t istgt_read_socket(int s, void *buf, size_t nbytes, int UNUSED_POLLWAIT(timeout)) { ssize_t n; #ifdef USE_POLLWAIT int msec = POLLWAIT; int rc; #endif /* USE_POLLWAIT */ if (nbytes == 0) return 0; #ifdef USE_POLLWAIT msec = timeout * 1000; rc = can_read_socket(s, msec); if (rc < 0) { return -1; } if (rc == 0) { /* TIMEOUT */ return -2; } retry: do { n = read(s, buf, nbytes); } while (n == -1 && errno == EINTR); if (n == -1 && errno == EAGAIN) { goto retry; } if (n < 0) { return -1; } #else do { n = recv(s, buf, nbytes, 0); } while (n == -1 && errno == EINTR); if (n == -1 && errno == EAGAIN) { /* TIMEOUT */ return -2; } if (n == -1) { return -1; } #endif /* USE_POLLWAIT */ return n; } ssize_t istgt_write_socket(int s, const void *buf, size_t nbytes, int UNUSED_POLLWAIT(timeout)) { ssize_t n; #ifdef USE_POLLWAIT int msec = POLLWAIT; int rc; #endif /* USE_POLLWAIT */ if (nbytes == 0) return 0; #ifdef USE_POLLWAIT msec = timeout * 1000; rc = can_write_socket(s, msec); if (rc < 0) { return -1; } if (rc == 0) { /* TIMEOUT */ return -2; } retry: do { n = write(s, buf, nbytes); } while (n == -1 && errno == EINTR); if (n == -1 && errno == EAGAIN) { goto retry; } if (n < 0) { return -1; } #else do { n = send(s, buf, nbytes, 0); } while (n == -1 && (errno == EINTR || errno == EAGAIN)); if (n == -1) { return -1; } #endif /* USE_POLLWAIT */ return n; } ssize_t istgt_readline_socket(int sock, char *buf, size_t size, char *tmp, size_t tmpsize, int *tmpidx, int *tmpcnt, int timeout) { unsigned char *up, *utp; ssize_t maxsize; ssize_t total; ssize_t n; int got_cr; int idx, cnt; int ch; if (size < 2) { return -1; } up = (unsigned char *) buf; utp = (unsigned char *) tmp; maxsize = size - 2; /* LF + NUL */ total = 0; idx = *tmpidx; cnt = *tmpcnt; got_cr = 0; /* receive with LF */ while (total < maxsize) { /* fill temporary buffer */ if (idx == cnt) { *tmpidx = idx; up[total] = '\0'; n = istgt_read_socket(sock, tmp, tmpsize, timeout); if (n < 0) { if (total != 0) { up[total] = '\0'; return total; } return -1; } if (n == 0) { /* EOF */ up[total] = '\0'; return total; } /* got n bytes */ cnt = *tmpcnt = n; idx = 0; } /* copy from temporary until LF */ ch = utp[idx++]; if (got_cr && ch != '\n') { /* CR only */ /* back to temporary */ idx--; /* remove CR */ total--; break; } else if (ch == '\n') { if (got_cr) { /* CRLF */ /* remove CR */ total--; } else { /* LF only */ } break; } else if (ch == '\r') { got_cr = 1; } up[total++] = ch; } *tmpidx = idx; /* always append LF + NUL */ up[total++] = '\n'; up[total] = '\0'; return total; } static ssize_t istgt_allwrite_socket(int s, const void *buf, size_t nbytes, int timeout) { const uint8_t *cp; size_t total; ssize_t n; total = 0; cp = (const uint8_t *) buf; do { n = istgt_write_socket(s, cp + total, (nbytes - total), timeout); if (n < 0) { return n; } total += n; } while (total < nbytes); return total; } ssize_t istgt_writeline_socket(int sock, const char *buf, int timeout) { const unsigned char *up; ssize_t total; ssize_t n; int idx; int ch; up = (const unsigned char *) buf; total = 0; idx = 0; if (up[0] == '\0') { /* empty string */ n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } total = n; return total; } /* send with CRLF */ while ((ch = up[idx]) != '\0') { if (ch == '\r') { if (up[idx + 1] == '\n') { /* CRLF */ n = istgt_allwrite_socket(sock, up, idx + 2, timeout); if (n < 0) { return -1; } if (n != idx + 2) { return -1; } idx += 2; } else { /* CR Only */ n = istgt_allwrite_socket(sock, up, idx, timeout); if (n < 0) { return -1; } if (n != idx) { return -1; } idx += 1; n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } } } else if (ch == '\n') { /* LF Only */ n = istgt_allwrite_socket(sock, up, idx, timeout); if (n < 0) { return -1; } if (n != idx) { return -1; } idx += 1; n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } } else { idx++; continue; } up += idx; total += idx; idx = 0; } if (idx != 0) { /* no CRLF string */ n = istgt_allwrite_socket(sock, up, idx, timeout); if (n < 0) { return -1; } if (n != idx) { return -1; } n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } up += idx; total += idx + 2; idx = 0; } return total; }