/* * Copyright © 2005,2008 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions of the * GNU General Public License v.2. This program is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY expressed or implied, * including the implied warranties of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat * trademarks that are incorporated in the source code or documentation are not * subject to the GNU General Public License and may only be used or replicated * with the express permission of Red Hat, Inc. * * Red Hat Author(s): Nathan Straz * Dean Jansa */ #include #include #include #include #include #include #include #include #include #include #include #include #include "qarsh_packet.h" static int packet_seq = 1; /* Some generic socket related functions to make things easier */ int getsockport(int sd) { struct sockaddr_storage addr; socklen_t addrlen; addrlen = sizeof addr; if (getsockname(sd, (struct sockaddr *)&addr, &addrlen) == 0) { if (addr.ss_family == AF_INET) { struct sockaddr_in *ipv4p = (struct sockaddr_in *)&addr; return ntohs(ipv4p->sin_port); } else { struct sockaddr_in6 *ipv6p = (struct sockaddr_in6 *)&addr; return ntohs(ipv6p->sin6_port); } } else { return -1; } } int bind_any(int minport, unsigned short ss_family) { static int lastminport = 0; static int nextport = 0; int sd; struct sockaddr_storage addr; if (minport == lastminport) { minport = nextport; } else { lastminport = minport; } sd = socket(ss_family, SOCK_STREAM, 0); if (sd == -1) return -1; if (ss_family == AF_INET) { struct sockaddr_in *ipv4p = (struct sockaddr_in *)&addr; ipv4p->sin_family = AF_INET; ipv4p->sin_addr.s_addr = htonl(INADDR_ANY); do { ipv4p->sin_port = htons(minport++); } while (bind(sd, (struct sockaddr *)&addr, sizeof addr) != 0); } else { struct sockaddr_in6 *ipv6p = (struct sockaddr_in6 *)&addr; ipv6p->sin6_family = AF_INET6; ipv6p->sin6_addr = in6addr_any; do { ipv6p->sin6_port = htons(minport++); } while (bind(sd, (struct sockaddr *)&addr, sizeof addr) != 0); } nextport = minport; if (listen(sd, 0) == -1) { syslog(LOG_WARNING, "listen error %d, %s", errno, strerror(errno)); } return sd; } int connect_to_host(char *hostname, int port, unsigned short *ss_family) { struct addrinfo *ail; struct addrinfo *aip; struct addrinfo hints; char portstr[NI_MAXSERV]; int sd; int err; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; memset(portstr, 0, sizeof portstr); snprintf(portstr, NI_MAXSERV, "%d", port); if ((err = getaddrinfo(hostname, portstr, &hints, &ail)) != 0) { fprintf(stderr, "Could not resolve hostname %s: %s\n", hostname, gai_strerror(err)); return -1; } /* TBD -- do we loop over all of the addrinfos returned trying to * connect, or just pick the first one? */ for (aip = ail; aip != NULL; aip = aip->ai_next) { #if 0 char hname[NI_MAXHOST] = ""; char nname[NI_MAXHOST] = ""; err = getnameinfo(aip->ai_addr, aip->ai_addrlen, hname, NI_MAXHOST, NULL, 0, 0); err = getnameinfo(aip->ai_addr, aip->ai_addrlen, nname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (err != 0) { printf("error in getnameinfo: %s\n", gai_strerror(err)); continue; } if (*hname && *nname) { printf("Trying: %s (%s) -- ", hname, nname); if (aip->ai_family == AF_INET6) puts("IPv6"); if (aip->ai_family == AF_INET) puts("IPv4"); } #endif sd = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if (sd == -1) return -1; if (connect(sd, aip->ai_addr, aip->ai_addrlen) == -1) { close(sd); continue; } else { *ss_family=aip->ai_family; freeaddrinfo(ail); return sd; } } freeaddrinfo(ail); return -1; } int connect_to_peer(struct sockaddr_storage *peer, int port) { int sd; int fdflags; struct sockaddr_in ipv4; struct sockaddr_in6 ipv6; struct sockaddr_storage *peeraddr; if (peer->ss_family == AF_INET) { ipv4.sin_family = AF_INET; ipv4.sin_port=htons(port); ipv4.sin_addr = ((struct sockaddr_in *)peer)->sin_addr; peeraddr = (struct sockaddr_storage *)&ipv4; } else { ipv6.sin6_family = AF_INET6; ipv6.sin6_port=htons(port); ipv6.sin6_addr = ((struct sockaddr_in6 *)peer)->sin6_addr; peeraddr = (struct sockaddr_storage *)&ipv6; } sd = socket(peer->ss_family, SOCK_STREAM, 0); if (sd == -1) return -1; /* Set close-on-exec for these sds */ if ((fdflags = fcntl(sd, F_GETFD, 0)) < 0) { return -1; } fdflags |= FD_CLOEXEC; if (fcntl(sd, F_SETFD, fdflags) < 0) { return -1; } if (connect(sd, (struct sockaddr *)peeraddr, sizeof *peeraddr) == -1) { return -1; } return sd; } /* * All incoming and outgoing packets go through this function. * Caller should make sure the fd is ready to read. */ struct qa_packet * recv_packet(int fd) { char buf[QARSH_MAX_PACKET_SIZE]; uint32_t packetsize; int bufused = 0; int ret = 0; if ((ret = read(fd, &packetsize, sizeof packetsize)) < 0) { if (errno != ECONNRESET) { fprintf(stderr, "Read error while reading packet size: %s\n", strerror(errno)); } return NULL; } else if (ret == 0) { return NULL; } else if (ret != sizeof packetsize) { fprintf(stderr, "Did not read packet size"); return NULL; } packetsize = ntohl(packetsize); if (packetsize > QARSH_MAX_PACKET_SIZE) { fprintf(stderr, "Packet size too large, %d > %d\n", packetsize, QARSH_MAX_PACKET_SIZE); return NULL; } /* Keep reading until we get the whole packet and nothing but the packet, so help me socket */ do { if ((ret = read(fd, buf+bufused, packetsize-bufused)) < 0) { fprintf(stderr, "Read error while reading packet data: %s\n", strerror(errno)); return NULL; } bufused += ret; } while (bufused < packetsize); return parse_packet(buf, packetsize); } int send_packet(int fd, struct qa_packet *qp) { char buf[QARSH_MAX_PACKET_SIZE]; uint32_t netsize; int len; ssize_t ret = -1; struct iovec iovs[2]; qp->qp_seq = packet_seq++; len = qptostr(qp, buf, QARSH_MAX_PACKET_SIZE - sizeof netsize); if (len > 0) { netsize = htonl(len); iovs[0].iov_base = &netsize; iovs[0].iov_len = sizeof netsize; iovs[1].iov_base = buf; iovs[1].iov_len = len; ret = writev(fd, iovs, 2); } return ret; }