static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd, const struct sockaddr *addr, socklen_t addrlen) { struct sockaddr_storage peer; socklen_t peerlen = sizeof(peer); fd_set wr_set, rd_set; int maxfd = sockfd; set_sock_nonblock(sockfd); if (vpninfo->protect_socket) vpninfo->protect_socket(vpninfo->cbdata, sockfd); if (connect(sockfd, addr, addrlen) < 0 && !connect_pending()) return -1; do { FD_ZERO(&wr_set); FD_ZERO(&rd_set); FD_SET(sockfd, &wr_set); cmd_fd_set(vpninfo, &rd_set, &maxfd); select(maxfd + 1, &rd_set, &wr_set, NULL, NULL); if (is_cancel_pending(vpninfo, &rd_set)) { vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n")); errno = EINTR; return -1; } } while (!FD_ISSET(sockfd, &wr_set) && !vpninfo->got_pause_cmd); /* Check whether connect() succeeded or failed by using getpeername(). See http://cr.yp.to/docs/connect.html */ return getpeername(sockfd, (void *)&peer, &peerlen); }
int do_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, int timeout) { set_sock_nonblock(sockfd); // Trying to connect with timeout int result = connect(sockfd, serv_addr, addrlen); if (result < 0) { // Not connected if (errno != EINPROGRESS) { // Not in progress return -1; } else { // Wait a timeout ms for socket to become ready to write into struct pollfd fdset[1]; fdset[0].fd = sockfd; fdset[0].events = POLLOUT | POLLERR | POLLHUP | POLLNVAL | POLLRDHUP; do { int fdready = poll(fdset, 1, timeout); if (fdready == -1 && errno == EINTR) continue; if (fdready < 1 || (fdready == 1 && fdset[0].revents != POLLOUT)) { // Timeout || error // Get socket error unsigned int err_val=0, sz = sizeof(int); getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&err_val), &sz); errno = err_val; if (!errno) // It can't be success, so simulate timeout errno = ENETDOWN; return -1; } break; } while (1); } } set_sock_block(sockfd); return 0; }
OPENCONNECT_CMD_SOCKET openconnect_setup_cmd_pipe(struct openconnect_info *vpninfo) { OPENCONNECT_CMD_SOCKET pipefd[2]; #ifdef _WIN32 if (dumb_socketpair(pipefd, 0)) return CMD_PIPE_ERR; #else if (pipe(pipefd) < 0) return CMD_PIPE_ERR; #endif if (set_sock_nonblock(pipefd[0]) || set_sock_nonblock(pipefd[1])) { closesocket(pipefd[0]); closesocket(pipefd[1]); return CMD_PIPE_ERR; } vpninfo->cmd_fd = pipefd[0]; vpninfo->cmd_fd_write = pipefd[1]; return vpninfo->cmd_fd_write; }
int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, int tun_fd) { set_fd_cloexec(tun_fd); if (vpninfo->tun_fd != -1) unmonitor_read_fd(vpninfo, tun); vpninfo->tun_fd = tun_fd; monitor_fd_new(vpninfo, tun); monitor_read_fd(vpninfo, tun); set_sock_nonblock(tun_fd); return 0; }
static int get_socket(const char *hostname, const char *service, int socktype, struct sockaddr_storage *addr, int *addrlen, int *sock, bool is_output) { struct addrinfo hints, *res, *ressave; int n, ret = -1; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = ai_family; hints.ai_socktype = socktype; n = getaddrinfo(hostname, service, &hints, &res); if (n < 0) { ts_LOGf("ERROR: getaddrinfo(%s): %s\n", hostname, gai_strerror(n)); return ret; } ressave = res; while (res) { *sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (*sock > -1) { int on = 1; setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); set_sock_nonblock(*sock); if (is_output) { memcpy(addr, res->ai_addr, res->ai_addrlen); *addrlen = res->ai_addrlen; ret = 0; break; } if (bind(*sock, res->ai_addr, res->ai_addrlen) == 0) { memcpy(addr, res->ai_addr, res->ai_addrlen); *addrlen = res->ai_addrlen; ret = 0; break; } else { char str_addr[INET6_ADDRSTRLEN]; my_inet_ntop(res->ai_family, res->ai_addr, str_addr, sizeof(str_addr)); ts_LOGf("ERROR: bind: %s:%s (%s): %s\n", hostname, service, str_addr, strerror(errno)); } close(*sock); *sock = -1; } res = res->ai_next; } freeaddrinfo(ressave); return ret; }
/* Windows is interminably horrid, and has disjoint errno spaces. * So if we return a positive value, that's a WSA Error and should * be handled with openconnect__win32_strerror(). But if we return a * negative value, that's a normal errno and should be handled with * strerror(). No, you can't just pass the latter value (negated) to * openconnect__win32_strerror() because it gives nonsense results. */ static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd, const struct sockaddr *addr, socklen_t addrlen) { struct sockaddr_storage peer; socklen_t peerlen = sizeof(peer); fd_set wr_set, rd_set, ex_set; int maxfd = sockfd; int err; set_sock_nonblock(sockfd); if (vpninfo->protect_socket) vpninfo->protect_socket(vpninfo->cbdata, sockfd); if (connect(sockfd, addr, addrlen) < 0 && !connect_pending()) { #ifdef _WIN32 return WSAGetLastError(); #else return -errno; #endif } do { FD_ZERO(&wr_set); FD_ZERO(&rd_set); FD_ZERO(&ex_set); FD_SET(sockfd, &wr_set); #ifdef _WIN32 /* Windows indicates failure this way, not in wr_set */ FD_SET(sockfd, &ex_set); #endif cmd_fd_set(vpninfo, &rd_set, &maxfd); select(maxfd + 1, &rd_set, &wr_set, &ex_set, NULL); if (is_cancel_pending(vpninfo, &rd_set)) { vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n")); return -EINTR; } } while (!FD_ISSET(sockfd, &wr_set) && !FD_ISSET(sockfd, &ex_set) && !vpninfo->got_pause_cmd); /* Check whether connect() succeeded or failed by using getpeername(). See http://cr.yp.to/docs/connect.html */ if (!getpeername(sockfd, (void *)&peer, &peerlen)) return 0; #ifdef _WIN32 /* On Windows, use getsockopt() to determine the error. * We don't ddo this on Windows because it just reports * -ENOTCONN, which we already knew. */ err = WSAGetLastError(); if (err == WSAENOTCONN) { socklen_t errlen = sizeof(err); getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen); } #else err = -errno; if (err == -ENOTCONN) { int ch; if (read(sockfd, &ch, 1) < 0) err = -errno; /* It should *always* fail! */ } #endif return err; }
int connect_dtls_socket(struct openconnect_info *vpninfo) { int dtls_fd, ret, sndbuf; /* Sanity check for the removal of new_dtls_{fd,ssl} */ if (vpninfo->dtls_fd != -1) { vpn_progress(vpninfo, PRG_ERR, _("DTLS connection attempted with an existing fd\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } if (!vpninfo->dtls_addr) { vpn_progress(vpninfo, PRG_ERR, _("No DTLS address\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } if (!vpninfo->dtls_cipher) { /* We probably didn't offer it any ciphers it liked */ vpn_progress(vpninfo, PRG_ERR, _("Server offered no DTLS cipher option\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } if (vpninfo->proxy) { /* XXX: Theoretically, SOCKS5 proxies can do UDP too */ vpn_progress(vpninfo, PRG_ERR, _("No DTLS when connected via proxy\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } dtls_fd = socket(vpninfo->peer_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); if (dtls_fd < 0) { perror(_("Open UDP socket for DTLS:")); return -EINVAL; } if (vpninfo->protect_socket) vpninfo->protect_socket(vpninfo->cbdata, dtls_fd); sndbuf = vpninfo->ip_info.mtu * 2; setsockopt(dtls_fd, SOL_SOCKET, SO_SNDBUF, (void *)&sndbuf, sizeof(sndbuf)); if (vpninfo->dtls_local_port) { union { struct sockaddr_in in; struct sockaddr_in6 in6; } dtls_bind_addr; int dtls_bind_addrlen; memset(&dtls_bind_addr, 0, sizeof(dtls_bind_addr)); if (vpninfo->peer_addr->sa_family == AF_INET) { struct sockaddr_in *addr = &dtls_bind_addr.in; dtls_bind_addrlen = sizeof(*addr); addr->sin_family = AF_INET; addr->sin_addr.s_addr = INADDR_ANY; addr->sin_port = htons(vpninfo->dtls_local_port); } else if (vpninfo->peer_addr->sa_family == AF_INET6) { struct sockaddr_in6 *addr = &dtls_bind_addr.in6; dtls_bind_addrlen = sizeof(*addr); addr->sin6_family = AF_INET6; addr->sin6_addr = in6addr_any; addr->sin6_port = htons(vpninfo->dtls_local_port); } else { vpn_progress(vpninfo, PRG_ERR, _("Unknown protocol family %d. Cannot do DTLS\n"), vpninfo->peer_addr->sa_family); vpninfo->dtls_attempt_period = 0; closesocket(dtls_fd); return -EINVAL; } if (bind(dtls_fd, (struct sockaddr *)&dtls_bind_addr, dtls_bind_addrlen)) { perror(_("Bind UDP socket for DTLS")); closesocket(dtls_fd); return -EINVAL; } } if (connect(dtls_fd, vpninfo->dtls_addr, vpninfo->peer_addrlen)) { perror(_("UDP (DTLS) connect:\n")); closesocket(dtls_fd); return -EINVAL; } set_fd_cloexec(dtls_fd); set_sock_nonblock(dtls_fd); ret = start_dtls_handshake(vpninfo, dtls_fd); if (ret) { closesocket(dtls_fd); return ret; } vpninfo->dtls_state = DTLS_CONNECTING; vpninfo->dtls_fd = dtls_fd; monitor_fd_new(vpninfo, dtls); monitor_read_fd(vpninfo, dtls); monitor_except_fd(vpninfo, dtls); time(&vpninfo->new_dtls_started); return dtls_try_handshake(vpninfo); }