connect.c | connect.c | |||
---|---|---|---|---|
/* | /* | |||
* connect.c - handles connections to ssh servers | * connect.c - handles connections to ssh servers | |||
* | * | |||
* This file is part of the SSH Library | * This file is part of the SSH Library | |||
* | * | |||
* Copyright (c) 2003-2008 by Aris Adamantiadis | * Copyright (c) 2003-2009 by Aris Adamantiadis | |||
* | * | |||
* The SSH Library is free software; you can redistribute it and/or modify | * The SSH Library is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as published by | * it under the terms of the GNU Lesser General Public License as published by | |||
* the Free Software Foundation; either version 2.1 of the License, or (at your | * the Free Software Foundation; either version 2.1 of the License, or (at your | |||
* option) any later version. | * option) any later version. | |||
* | * | |||
* The SSH Library is distributed in the hope that it will be useful, but | * The SSH Library is distributed in the hope that it will be useful, but | |||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILI TY | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILI TY | |||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | |||
* License for more details. | * License for more details. | |||
skipping to change at line 29 | skipping to change at line 29 | |||
* along with the SSH Library; see the file COPYING. If not, write to | * along with the SSH Library; see the file COPYING. If not, write to | |||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | |||
* MA 02111-1307, USA. | * MA 02111-1307, USA. | |||
*/ | */ | |||
#include <errno.h> | #include <errno.h> | |||
#include <fcntl.h> | #include <fcntl.h> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <string.h> | #include <string.h> | |||
#include <unistd.h> | ||||
#ifdef _WIN32 | #ifdef _WIN32 | |||
/* getaddrinfo, freeaddrinfo, getnameinfo */ | /* | |||
#define _WIN32_WINNT 0x0501 | * Only use Windows API functions available on Windows 2000 SP4 or later. | |||
* The available constants are in <sdkddkver.h>. | ||||
* http://msdn.microsoft.com/en-us/library/aa383745.aspx | ||||
* http://blogs.msdn.com/oldnewthing/archive/2007/04/11/2079137.aspx | ||||
*/ | ||||
#undef _WIN32_WINNT | ||||
#ifdef HAVE_WSPIAPI_H | ||||
#define _WIN32_WINNT 0x0500 /* _WIN32_WINNT_WIN2K */ | ||||
#undef NTDDI_VERSION | ||||
#define NTDDI_VERSION 0x05000400 /* NTDDI_WIN2KSP4 */ | ||||
#else | ||||
#define _WIN32_WINNT 0x0501 /* _WIN32_WINNT_WINXP */ | ||||
#undef NTDDI_VERSION | ||||
#define NTDDI_VERSION 0x05010000 /* NTDDI_WINXP */ | ||||
#endif | ||||
#include <winsock2.h> | #include <winsock2.h> | |||
#include <ws2tcpip.h> | #include <ws2tcpip.h> | |||
#include "wspiapi.h" /* Workaround for w2k systems */ | /* <wspiapi.h> is necessary for getaddrinfo before Windows XP, but it isn't | |||
* available on some platforms like MinGW. */ | ||||
#ifdef HAVE_WSPIAPI_H | ||||
#include <wspiapi.h> | ||||
#endif | ||||
#else /* _WIN32 */ | #else /* _WIN32 */ | |||
#include <netdb.h> | #include <netdb.h> | |||
#include <sys/socket.h> | #include <sys/socket.h> | |||
#include <sys/select.h> | #include <sys/select.h> | |||
#include <netinet/in.h> | #include <netinet/in.h> | |||
#endif /* _WIN32 */ | #endif /* _WIN32 */ | |||
#include "libssh/priv.h" | #include "libssh/priv.h" | |||
#include "libssh/socket.h" | ||||
#include "libssh/channels.h" | ||||
#include "libssh/session.h" | ||||
#ifndef HAVE_SELECT | #ifndef HAVE_SELECT | |||
#error "Your system must have select()" | #error "Your system must have select()" | |||
#endif | #endif | |||
#ifndef HAVE_GETADDRINFO | #ifndef HAVE_GETADDRINFO | |||
#error "Your system must have getaddrinfo()" | #error "Your system must have getaddrinfo()" | |||
#endif | #endif | |||
#ifdef HAVE_REGCOMP | ||||
/* don't declare gnu extended regexp's */ | ||||
#ifndef _POSIX_C_SOURCE | ||||
#define _POSIX_C_SOURCE | ||||
#endif | ||||
#include <regex.h> | ||||
#endif /* HAVE_REGCOMP */ | ||||
#ifdef _WIN32 | #ifdef _WIN32 | |||
static void sock_set_nonblocking(socket_t sock) { | static void sock_set_nonblocking(socket_t sock) { | |||
u_long nonblocking = 1; | u_long nonblocking = 1; | |||
ioctlsocket(sock, FIONBIO, &nonblocking); | ioctlsocket(sock, FIONBIO, &nonblocking); | |||
} | } | |||
static void sock_set_blocking(socket_t sock) { | static void sock_set_blocking(socket_t sock) { | |||
u_long nonblocking = 0; | u_long nonblocking = 0; | |||
ioctlsocket(sock, FIONBIO, &nonblocking); | ioctlsocket(sock, FIONBIO, &nonblocking); | |||
} | } | |||
skipping to change at line 88 | skipping to change at line 116 | |||
#endif /* gai_strerror */ | #endif /* gai_strerror */ | |||
#else /* _WIN32 */ | #else /* _WIN32 */ | |||
static void sock_set_nonblocking(socket_t sock) { | static void sock_set_nonblocking(socket_t sock) { | |||
fcntl(sock, F_SETFL, O_NONBLOCK); | fcntl(sock, F_SETFL, O_NONBLOCK); | |||
} | } | |||
static void sock_set_blocking(socket_t sock) { | static void sock_set_blocking(socket_t sock) { | |||
fcntl(sock, F_SETFL, 0); | fcntl(sock, F_SETFL, 0); | |||
} | } | |||
#endif /* _WIN32 */ | #endif /* _WIN32 */ | |||
static int getai(const char *host, int port, struct addrinfo **ai) { | #ifdef HAVE_REGCOMP | |||
static regex_t *ip_regex = NULL; | ||||
/** @internal | ||||
* @brief initializes and compile the regexp to be used for IP matching | ||||
* @returns -1 on error (and error message is set) | ||||
* @returns 0 on success | ||||
*/ | ||||
int ssh_regex_init(){ | ||||
if(ip_regex==NULL){ | ||||
int err; | ||||
regex_t *regex=malloc(sizeof (regex_t)); | ||||
ZERO_STRUCTP(regex); | ||||
err=regcomp(regex,"^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$",REG_EXTENDED | | ||||
REG_NOSUB); | ||||
if(err != 0){ | ||||
char buffer[128]; | ||||
regerror(err,regex,buffer,sizeof(buffer)); | ||||
fprintf(stderr,"Error while compiling regular expression : %s\n",buff | ||||
er); | ||||
SAFE_FREE(regex); | ||||
return -1; | ||||
} | ||||
ip_regex=regex; | ||||
} | ||||
return 0; | ||||
} | ||||
/** @internal | ||||
* @brief clean up the IP regexp | ||||
*/ | ||||
void ssh_regex_finalize(){ | ||||
if(ip_regex){ | ||||
regfree(ip_regex); | ||||
SAFE_FREE(ip_regex); | ||||
} | ||||
} | ||||
#else /* HAVE_REGCOMP */ | ||||
int ssh_regex_init(){ | ||||
return 0; | ||||
} | ||||
void ssh_regex_finalize(){ | ||||
} | ||||
#endif | ||||
static int ssh_connect_socket_close(socket_t s){ | ||||
#ifdef _WIN32 | ||||
return closesocket(s); | ||||
#else | ||||
return close(s); | ||||
#endif | ||||
} | ||||
static int getai(ssh_session session, const char *host, int port, struct ad | ||||
drinfo **ai) { | ||||
const char *service = NULL; | const char *service = NULL; | |||
struct addrinfo hints; | struct addrinfo hints; | |||
char s_port[10]; | char s_port[10]; | |||
ZERO_STRUCT(hints); | ZERO_STRUCT(hints); | |||
hints.ai_protocol = IPPROTO_TCP; | hints.ai_protocol = IPPROTO_TCP; | |||
hints.ai_family = PF_UNSPEC; | hints.ai_family = PF_UNSPEC; | |||
hints.ai_socktype = SOCK_STREAM; | hints.ai_socktype = SOCK_STREAM; | |||
if (port == 0) { | if (port == 0) { | |||
hints.ai_flags = AI_PASSIVE; | hints.ai_flags = AI_PASSIVE; | |||
} else { | } else { | |||
snprintf(s_port, sizeof(s_port), "%hu", port); | snprintf(s_port, sizeof(s_port), "%hu", port); | |||
service = s_port; | service = s_port; | |||
#ifdef AI_NUMERICSERV | ||||
hints.ai_flags=AI_NUMERICSERV; | ||||
#endif | ||||
} | } | |||
#ifdef HAVE_REGCOMP | ||||
if(regexec(ip_regex,host,0,NULL,0) == 0){ | ||||
/* this is an IP address */ | ||||
ssh_log(session,SSH_LOG_PACKET,"host %s matches an IP address",host); | ||||
hints.ai_flags |= AI_NUMERICHOST; | ||||
} | ||||
#endif | ||||
return getaddrinfo(host, service, &hints, ai); | return getaddrinfo(host, service, &hints, ai); | |||
} | } | |||
static int ssh_connect_ai_timeout(SSH_SESSION *session, const char *host, | static int ssh_connect_ai_timeout(ssh_session session, const char *host, | |||
int port, struct addrinfo *ai, long timeout, long usec, socket_t s) { | int port, struct addrinfo *ai, long timeout, long usec, socket_t s) { | |||
struct timeval to; | struct timeval to; | |||
fd_set set; | fd_set set; | |||
int rc = 0; | int rc = 0; | |||
unsigned int len = sizeof(rc); | unsigned int len = sizeof(rc); | |||
enter_function(); | enter_function(); | |||
to.tv_sec = timeout; | to.tv_sec = timeout; | |||
to.tv_usec = usec; | to.tv_usec = usec; | |||
sock_set_nonblocking(s); | sock_set_nonblocking(s); | |||
ssh_log(session, SSH_LOG_RARE, "Trying to connect to host: %s:%d with " | ||||
"timeout %ld.%ld", host, port, timeout, usec); | ||||
/* The return value is checked later */ | /* The return value is checked later */ | |||
connect(s, ai->ai_addr, ai->ai_addrlen); | connect(s, ai->ai_addr, ai->ai_addrlen); | |||
freeaddrinfo(ai); | freeaddrinfo(ai); | |||
FD_ZERO(&set); | FD_ZERO(&set); | |||
FD_SET(s, &set); | FD_SET(s, &set); | |||
rc = select(s + 1, NULL, &set, NULL, &to); | rc = select(s + 1, NULL, &set, NULL, &to); | |||
if (rc == 0) { | if (rc == 0) { | |||
/* timeout */ | /* timeout */ | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"Timeout while connecting to %s:%d", host, port); | "Timeout while connecting to %s:%d", host, port); | |||
close(s); | ssh_connect_socket_close(s); | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
if (rc < 0) { | if (rc < 0) { | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"Select error: %s", strerror(errno)); | "Select error: %s", strerror(errno)); | |||
close(s); | ssh_connect_socket_close(s); | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
rc = 0; | rc = 0; | |||
/* Get connect(2) return code. Zero means no error */ | /* Get connect(2) return code. Zero means no error */ | |||
getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len); | getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len); | |||
if (rc != 0) { | if (rc != 0) { | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"Connect to %s:%d failed: %s", host, port, strerror(rc)); | "Connect to %s:%d failed: %s", host, port, strerror(rc)); | |||
close(s); | ssh_connect_socket_close(s); | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
/* s is connected ? */ | /* s is connected ? */ | |||
ssh_log(session, SSH_LOG_PACKET, "Socket connected with timeout\n"); | ssh_log(session, SSH_LOG_PACKET, "Socket connected with timeout\n"); | |||
sock_set_blocking(s); | sock_set_blocking(s); | |||
leave_function(); | leave_function(); | |||
return s; | return s; | |||
} | } | |||
/** | /** | |||
* @internal | * @internal | |||
* | * | |||
* @brief Connect to an IPv4 or IPv6 host specified by its IP address or | * @brief Connect to an IPv4 or IPv6 host specified by its IP address or | |||
* hostname. | * hostname. | |||
* | * | |||
* @returns A file descriptor, < 0 on error. | * @returns A file descriptor, < 0 on error. | |||
*/ | */ | |||
socket_t ssh_connect_host(SSH_SESSION *session, const char *host, | socket_t ssh_connect_host(ssh_session session, const char *host, | |||
const char *bind_addr, int port, long timeout, long usec) { | const char *bind_addr, int port, long timeout, long usec) { | |||
socket_t s = -1; | socket_t s = -1; | |||
int rc; | int rc; | |||
struct addrinfo *ai; | struct addrinfo *ai; | |||
struct addrinfo *itr; | struct addrinfo *itr; | |||
enter_function(); | enter_function(); | |||
rc = getai(host, port, &ai); | rc = getai(session,host, port, &ai); | |||
if (rc != 0) { | if (rc != 0) { | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); | "Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
for (itr = ai; itr != NULL; itr = itr->ai_next){ | for (itr = ai; itr != NULL; itr = itr->ai_next){ | |||
/* create socket */ | /* create socket */ | |||
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol); | s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol); | |||
skipping to change at line 209 | skipping to change at line 302 | |||
"Socket create failed: %s", strerror(errno)); | "Socket create failed: %s", strerror(errno)); | |||
continue; | continue; | |||
} | } | |||
if (bind_addr) { | if (bind_addr) { | |||
struct addrinfo *bind_ai; | struct addrinfo *bind_ai; | |||
struct addrinfo *bind_itr; | struct addrinfo *bind_itr; | |||
ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr); | ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr); | |||
rc = getai(host, 0, &bind_ai); | rc = getai(session,bind_addr, 0, &bind_ai); | |||
if (rc != 0) { | if (rc != 0) { | |||
ssh_set_error(session, SSH_FATAL, | ssh_set_error(session, SSH_FATAL, | |||
"Failed to resolve bind address %s (%s)", | "Failed to resolve bind address %s (%s)", | |||
bind_addr, | bind_addr, | |||
gai_strerror(rc)); | gai_strerror(rc)); | |||
leave_function(); | leave_function(); | |||
return -1; | return -1; | |||
} | } | |||
for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_ne xt) { | for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_ne xt) { | |||
skipping to change at line 232 | skipping to change at line 325 | |||
"Binding local address: %s", strerror(errno)); | "Binding local address: %s", strerror(errno)); | |||
continue; | continue; | |||
} else { | } else { | |||
break; | break; | |||
} | } | |||
} | } | |||
freeaddrinfo(bind_ai); | freeaddrinfo(bind_ai); | |||
/* Cannot bind to any local addresses */ | /* Cannot bind to any local addresses */ | |||
if (bind_itr == NULL) { | if (bind_itr == NULL) { | |||
close(s); | ssh_connect_socket_close(s); | |||
s = -1; | s = -1; | |||
continue; | continue; | |||
} | } | |||
} | } | |||
if (timeout || usec) { | if (timeout || usec) { | |||
socket_t ret = ssh_connect_ai_timeout(session, host, port, itr, | socket_t ret = ssh_connect_ai_timeout(session, host, port, itr, | |||
timeout, usec, s); | timeout, usec, s); | |||
leave_function(); | leave_function(); | |||
return ret; | return ret; | |||
} | } | |||
if (connect(s, itr->ai_addr, itr->ai_addrlen) < 0) { | if (connect(s, itr->ai_addr, itr->ai_addrlen) < 0) { | |||
ssh_set_error(session, SSH_FATAL, "Connect failed: %s", strerror(errn o)); | ssh_set_error(session, SSH_FATAL, "Connect failed: %s", strerror(errn o)); | |||
close(s); | ssh_connect_socket_close(s); | |||
s = -1; | s = -1; | |||
leave_function(); | leave_function(); | |||
continue; | continue; | |||
} else { | } else { | |||
/* We are connected */ | /* We are connected */ | |||
break; | break; | |||
} | } | |||
} | } | |||
freeaddrinfo(ai); | freeaddrinfo(ai); | |||
skipping to change at line 295 | skipping to change at line 388 | |||
* | * | |||
* @return -1 if an error occured. E_INTR if it was interrupted. In that ca se, | * @return -1 if an error occured. E_INTR if it was interrupted. In that ca se, | |||
* just restart it. | * just restart it. | |||
* | * | |||
* @warning libssh is not threadsafe here. That means that if a signal is c aught | * @warning libssh is not threadsafe here. That means that if a signal is c aught | |||
* during the processing of this function, you cannot call ssh functions on | * during the processing of this function, you cannot call ssh functions on | |||
* sessions that are busy with ssh_select(). | * sessions that are busy with ssh_select(). | |||
* | * | |||
* @see select(2) | * @see select(2) | |||
*/ | */ | |||
int ssh_select(CHANNEL **channels, CHANNEL **outchannels, socket_t maxfd, | int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t ma xfd, | |||
fd_set *readfds, struct timeval *timeout) { | fd_set *readfds, struct timeval *timeout) { | |||
struct timeval zerotime; | struct timeval zerotime; | |||
fd_set localset, localset2; | fd_set localset, localset2; | |||
int rep; | int rep; | |||
int set; | int set; | |||
int i; | int i; | |||
int j; | int j; | |||
zerotime.tv_sec = 0; | zerotime.tv_sec = 0; | |||
zerotime.tv_usec = 0; | zerotime.tv_usec = 0; | |||
End of changes. 21 change blocks. | ||||
17 lines changed or deleted | 113 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |