/* $OpenBSD: b_sock.c,v 1.70 2022/12/22 20:13:45 schwarze Exp $ */ /* * Copyright (c) 2017 Bob Beck * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int BIO_get_host_ip(const char *str, unsigned char *ip) { struct addrinfo *res = NULL; struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE, }; uint32_t *iap = (in_addr_t *)ip; int error; if (str == NULL) { BIOerror(BIO_R_BAD_HOSTNAME_LOOKUP); ERR_asprintf_error_data("NULL host provided"); return (0); } if ((error = getaddrinfo(str, NULL, &hints, &res)) != 0) { BIOerror(BIO_R_BAD_HOSTNAME_LOOKUP); ERR_asprintf_error_data("getaddrinfo: host='%s' : %s'", str, gai_strerror(error)); return (0); } *iap = (uint32_t)(((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr); freeaddrinfo(res); return (1); } int BIO_get_port(const char *str, unsigned short *port_ptr) { struct addrinfo *res = NULL; struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE, }; int error; if (str == NULL) { BIOerror(BIO_R_NO_PORT_SPECIFIED); return (0); } if ((error = getaddrinfo(NULL, str, &hints, &res)) != 0) { BIOerror(BIO_R_INVALID_ARGUMENT); ERR_asprintf_error_data("getaddrinfo: service='%s' : %s'", str, gai_strerror(error)); return (0); } *port_ptr = ntohs(((struct sockaddr_in *)(res->ai_addr))->sin_port); freeaddrinfo(res); return (1); } int BIO_sock_error(int sock) { socklen_t len; int err; len = sizeof(err); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) != 0) return (1); return (err); } struct hostent * BIO_gethostbyname(const char *name) { return gethostbyname(name); } int BIO_socket_ioctl(int fd, long type, void *arg) { int ret; ret = ioctl(fd, type, arg); if (ret < 0) SYSerror(errno); return (ret); } int BIO_get_accept_socket(char *host, int bind_mode) { struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE, }; struct addrinfo *res = NULL; char *h, *p, *str = NULL; int error, ret = 0, s = -1; if (host == NULL) { BIOerror(BIO_R_NO_PORT_SPECIFIED); return (-1); } if ((str = strdup(host)) == NULL) { BIOerror(ERR_R_MALLOC_FAILURE); return (-1); } p = NULL; h = str; if ((p = strrchr(str, ':')) == NULL) { /* A string without a colon is treated as a port. */ p = str; h = NULL; } else { *p++ = '\0'; if (*p == '\0') { BIOerror(BIO_R_NO_PORT_SPECIFIED); goto err; } if (*h == '\0' || strcmp(h, "*") == 0) h = NULL; } if ((error = getaddrinfo(h, p, &hints, &res)) != 0) { BIOerror(BIO_R_BAD_HOSTNAME_LOOKUP); ERR_asprintf_error_data("getaddrinfo: '%s:%s': %s'", h, p, gai_strerror(error)); goto err; } if (h == NULL) { struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr; sin->sin_addr.s_addr = INADDR_ANY; } s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) { SYSerror(errno); ERR_asprintf_error_data("host='%s'", host); BIOerror(BIO_R_UNABLE_TO_CREATE_SOCKET); goto err; } if (bind_mode == BIO_BIND_REUSEADDR) { int i = 1; ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); bind_mode = BIO_BIND_NORMAL; } if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { SYSerror(errno); ERR_asprintf_error_data("host='%s'", host); BIOerror(BIO_R_UNABLE_TO_BIND_SOCKET); goto err; } if (listen(s, SOMAXCONN) == -1) { SYSerror(errno); ERR_asprintf_error_data("host='%s'", host); BIOerror(BIO_R_UNABLE_TO_LISTEN_SOCKET); goto err; } ret = 1; err: free(str); if (res != NULL) freeaddrinfo(res); if ((ret == 0) && (s != -1)) { close(s); s = -1; } return (s); } int BIO_accept(int sock, char **addr) { char h[NI_MAXHOST], s[NI_MAXSERV]; struct sockaddr_in sin; socklen_t sin_len = sizeof(sin); int ret = -1; if (addr == NULL) { BIOerror(BIO_R_NULL_PARAMETER); goto end; } ret = accept(sock, (struct sockaddr *)&sin, &sin_len); if (ret == -1) { if (BIO_sock_should_retry(ret)) return -2; SYSerror(errno); BIOerror(BIO_R_ACCEPT_ERROR); goto end; } /* XXX Crazy API. Can't be helped */ if (*addr != NULL) { free(*addr); *addr = NULL; } if (sin.sin_family != AF_INET) goto end; if (getnameinfo((struct sockaddr *)&sin, sin_len, h, sizeof(h), s, sizeof(s), NI_NUMERICHOST|NI_NUMERICSERV) != 0) goto end; if ((asprintf(addr, "%s:%s", h, s)) == -1) { BIOerror(ERR_R_MALLOC_FAILURE); *addr = NULL; goto end; } end: return (ret); } int BIO_set_tcp_ndelay(int s, int on) { return (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == 0); }