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/