1 /*
2  * socket.c - socket functions for the library
3  *
4  * This file is part of the SSH Library
5  *
6  * Copyright (c) 2008-2010      by Aris Adamantiadis
7  *
8  * The SSH Library is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or (at your
11  * option) any later version.
12  *
13  * The SSH Library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16  * License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with the SSH Library; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21  * MA 02111-1307, USA.
22  */
23 
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #ifdef _WIN32
29 #include <winsock2.h>
30 #include <ws2tcpip.h>
31 #if _MSC_VER >= 1400
32 #include <io.h>
33 #undef open
34 #define open _open
35 #undef close
36 #define close _close
37 #undef read
38 #define read _read
39 #undef write
40 #define write _write
41 #endif /* _MSC_VER */
42 #else /* _WIN32 */
43 #include <fcntl.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/un.h>
47 #endif /* _WIN32 */
48 
49 #include "libssh/priv.h"
50 #include "libssh/callbacks.h"
51 #include "libssh/socket.h"
52 #include "libssh/buffer.h"
53 #include "libssh/poll.h"
54 #include "libssh/session.h"
55 
56 /**
57  * @internal
58  *
59  * @defgroup libssh_socket The SSH socket functions.
60  * @ingroup libssh
61  *
62  * Functions for handling sockets.
63  *
64  * @{
65  */
66 
67 enum ssh_socket_states_e {
68 	SSH_SOCKET_NONE,
69 	SSH_SOCKET_CONNECTING,
70 	SSH_SOCKET_CONNECTED,
71 	SSH_SOCKET_EOF,
72 	SSH_SOCKET_ERROR,
73 	SSH_SOCKET_CLOSED
74 };
75 
76 struct ssh_socket_struct {
77   socket_t fd_in;
78   socket_t fd_out;
79   int fd_is_socket;
80   int last_errno;
81   int read_wontblock; /* reading now on socket will
82                        not block */
83   int write_wontblock;
84   int data_except;
85   enum ssh_socket_states_e state;
86   ssh_buffer out_buffer;
87   ssh_buffer in_buffer;
88   ssh_session session;
89   ssh_socket_callbacks callbacks;
90   ssh_poll_handle poll_in;
91   ssh_poll_handle poll_out;
92 };
93 
94 static int sockets_initialized = 0;
95 
96 static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len);
97 static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer,
98 		uint32_t len);
99 
100 /**
101  * \internal
102  * \brief inits the socket system (windows specific)
103  */
ssh_socket_init(void)104 int ssh_socket_init(void) {
105   if (sockets_initialized == 0) {
106 #ifdef _WIN32
107     struct WSAData wsaData;
108 
109     /* Initiates use of the Winsock DLL by a process. */
110     if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
111       return -1;
112     }
113 
114 #endif
115     ssh_poll_init();
116 
117     sockets_initialized = 1;
118   }
119 
120   return 0;
121 }
122 
123 /**
124  * @brief Cleanup the socket system.
125  */
ssh_socket_cleanup(void)126 void ssh_socket_cleanup(void) {
127   if (sockets_initialized == 1) {
128     ssh_poll_cleanup();
129 #ifdef _WIN32
130     WSACleanup();
131 #endif
132     sockets_initialized = 0;
133   }
134 }
135 
136 
137 /**
138  * \internal
139  * \brief creates a new Socket object
140  */
ssh_socket_new(ssh_session session)141 ssh_socket ssh_socket_new(ssh_session session) {
142   ssh_socket s;
143 
144   s = malloc(sizeof(struct ssh_socket_struct));
145   if (s == NULL) {
146     ssh_set_error_oom(session);
147     return NULL;
148   }
149   s->fd_in = SSH_INVALID_SOCKET;
150   s->fd_out= SSH_INVALID_SOCKET;
151   s->last_errno = -1;
152   s->fd_is_socket = 1;
153   s->session = session;
154   s->in_buffer = ssh_buffer_new();
155   if (s->in_buffer == NULL) {
156     ssh_set_error_oom(session);
157     SAFE_FREE(s);
158     return NULL;
159   }
160   s->out_buffer=ssh_buffer_new();
161   if (s->out_buffer == NULL) {
162     ssh_set_error_oom(session);
163     ssh_buffer_free(s->in_buffer);
164     SAFE_FREE(s);
165     return NULL;
166   }
167   s->read_wontblock = 0;
168   s->write_wontblock = 0;
169   s->data_except = 0;
170   s->poll_in=s->poll_out=NULL;
171   s->state=SSH_SOCKET_NONE;
172   return s;
173 }
174 
175 /**
176  * @internal
177  * @brief Reset the state of a socket so it looks brand-new
178  * @param[in] s socket to rest
179  */
ssh_socket_reset(ssh_socket s)180 void ssh_socket_reset(ssh_socket s){
181   s->fd_in = SSH_INVALID_SOCKET;
182   s->fd_out= SSH_INVALID_SOCKET;
183   s->last_errno = -1;
184   s->fd_is_socket = 1;
185   buffer_reinit(s->in_buffer);
186   buffer_reinit(s->out_buffer);
187   s->read_wontblock = 0;
188   s->write_wontblock = 0;
189   s->data_except = 0;
190   s->poll_in=s->poll_out=NULL;
191   s->state=SSH_SOCKET_NONE;
192 }
193 
194 /**
195  * @internal
196  * @brief the socket callbacks, i.e. callbacks to be called
197  * upon a socket event.
198  * @param s socket to set callbacks on.
199  * @param callbacks a ssh_socket_callback object reference.
200  */
201 
ssh_socket_set_callbacks(ssh_socket s,ssh_socket_callbacks callbacks)202 void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){
203 	s->callbacks=callbacks;
204 }
205 
206 /**
207  * @brief 							SSH poll callback. This callback will be used when an event
208  *                      caught on the socket.
209  *
210  * @param p             Poll object this callback belongs to.
211  * @param fd            The raw socket.
212  * @param revents       The current poll events on the socket.
213  * @param userdata      Userdata to be passed to the callback function,
214  *                      in this case the socket object.
215  *
216  * @return              0 on success, < 0 when the poll object has been removed
217  *                      from its poll context.
218  */
ssh_socket_pollcallback(struct ssh_poll_handle_struct * p,socket_t fd,int revents,void * v_s)219 int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){
220 	ssh_socket s=(ssh_socket )v_s;
221 	char buffer[4096];
222 	int r;
223 	int err=0;
224 	socklen_t errlen=sizeof(err);
225 	/* Do not do anything if this socket was already closed */
226 	if(!ssh_socket_is_open(s)){
227 	  return -1;
228 	}
229 	if(revents & POLLERR || revents & POLLHUP){
230 		/* Check if we are in a connecting state */
231 		if(s->state==SSH_SOCKET_CONNECTING){
232 			s->state=SSH_SOCKET_ERROR;
233 			getsockopt(fd,SOL_SOCKET,SO_ERROR,(char *)&err,&errlen);
234 			s->last_errno=err;
235 			ssh_socket_close(s);
236 			if(s->callbacks && s->callbacks->connected)
237 				s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,err,
238 						s->callbacks->userdata);
239 			return -1;
240 		}
241 		/* Then we are in a more standard kind of error */
242 		/* force a read to get an explanation */
243 		revents |= POLLIN;
244 	}
245 	if(revents & POLLIN){
246 		s->read_wontblock=1;
247 		r=ssh_socket_unbuffered_read(s,buffer,sizeof(buffer));
248 		if(r<0){
249 		if(p != NULL) {
250 			ssh_poll_remove_events(p, POLLIN);
251 		}
252 			if(s->callbacks && s->callbacks->exception){
253 				s->callbacks->exception(
254 						SSH_SOCKET_EXCEPTION_ERROR,
255 						s->last_errno,s->callbacks->userdata);
256 				/* p may have been freed, so don't use it
257 				* anymore in this function */
258 				p = NULL;
259 				return -2;
260 			}
261 		}
262 		if(r==0){
263 			if(p != NULL) {
264 				ssh_poll_remove_events(p, POLLIN);
265 			}
266 			if(p != NULL) {
267 				ssh_poll_remove_events(p, POLLIN);
268 			}
269 			if(s->callbacks && s->callbacks->exception){
270 				s->callbacks->exception(
271 						SSH_SOCKET_EXCEPTION_EOF,
272 						0,s->callbacks->userdata);
273 				/* p may have been freed, so don't use it
274 				* anymore in this function */
275 				p = NULL;
276 				return -2;
277 			}
278 		}
279 		if(r>0){
280 			/* Bufferize the data and then call the callback */
281 			buffer_add_data(s->in_buffer,buffer,r);
282 			if(s->callbacks && s->callbacks->data){
283 				r= s->callbacks->data(buffer_get_rest(s->in_buffer),
284 						buffer_get_rest_len(s->in_buffer),
285 						s->callbacks->userdata);
286 				buffer_pass_bytes(s->in_buffer,r);
287 				/* p may have been freed, so don't use it
288 				* anymore in this function */
289 				p = NULL;
290 			}
291 		}
292 	}
293 #ifdef _WIN32
294 	if(revents & POLLOUT || revents & POLLWRNORM){
295 #else
296 	if(revents & POLLOUT){
297 #endif
298 		/* First, POLLOUT is a sign we may be connected */
299 		if(s->state == SSH_SOCKET_CONNECTING){
300 			ssh_log(s->session,SSH_LOG_PACKET,"Received POLLOUT in connecting state");
301 			s->state = SSH_SOCKET_CONNECTED;
302 			ssh_poll_set_events(p,POLLOUT | POLLIN);
303 			ssh_socket_set_blocking(ssh_socket_get_fd_in(s));
304 			if(s->callbacks && s->callbacks->connected)
305 				s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata);
306 			return 0;
307 		}
308 		/* So, we can write data */
309 		s->write_wontblock=1;
310         if(p != NULL) {
311             ssh_poll_remove_events(p, POLLOUT);
312         }
313 
314 		/* If buffered data is pending, write it */
315 		if(buffer_get_rest_len(s->out_buffer) > 0){
316 		  ssh_socket_nonblocking_flush(s);
317 		} else if(s->callbacks && s->callbacks->controlflow){
318 			/* Otherwise advertise the upper level that write can be done */
319 			s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,s->callbacks->userdata);
320 		}
321 			/* TODO: Find a way to put back POLLOUT when buffering occurs */
322 	}
323 	/* Return -1 if one of the poll handlers disappeared */
324 	return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0;
325 }
326 
327 /** @internal
328  * @brief returns the input poll handle corresponding to the socket,
329  * creates it if it does not exist.
330  * @returns allocated and initialized ssh_poll_handle object
331  */
332 ssh_poll_handle ssh_socket_get_poll_handle_in(ssh_socket s){
333 	if(s->poll_in)
334 		return s->poll_in;
335 	s->poll_in=ssh_poll_new(s->fd_in,0,ssh_socket_pollcallback,s);
336 	if(s->fd_in == s->fd_out && s->poll_out == NULL)
337     s->poll_out=s->poll_in;
338 	return s->poll_in;
339 }
340 
341 /** @internal
342  * @brief returns the output poll handle corresponding to the socket,
343  * creates it if it does not exist.
344  * @returns allocated and initialized ssh_poll_handle object
345  */
346 ssh_poll_handle ssh_socket_get_poll_handle_out(ssh_socket s){
347   if(s->poll_out)
348     return s->poll_out;
349   s->poll_out=ssh_poll_new(s->fd_out,0,ssh_socket_pollcallback,s);
350   if(s->fd_in == s->fd_out && s->poll_in == NULL)
351     s->poll_in=s->poll_out;
352   return s->poll_out;
353 }
354 
355 /** \internal
356  * \brief Deletes a socket object
357  */
358 void ssh_socket_free(ssh_socket s){
359   if (s == NULL) {
360     return;
361   }
362   ssh_socket_close(s);
363   ssh_buffer_free(s->in_buffer);
364   ssh_buffer_free(s->out_buffer);
365   SAFE_FREE(s);
366 }
367 
368 #ifndef _WIN32
369 int ssh_socket_unix(ssh_socket s, const char *path) {
370   struct sockaddr_un sunaddr;
371   socket_t fd;
372   sunaddr.sun_family = AF_UNIX;
373   snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
374 
375   fd = socket(AF_UNIX, SOCK_STREAM, 0);
376   if (fd == SSH_INVALID_SOCKET) {
377     ssh_set_error(s->session, SSH_FATAL,
378 		    "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
379 		    strerror(errno));
380     return -1;
381   }
382 
383   if (fcntl(fd, F_SETFD, 1) == -1) {
384     ssh_set_error(s->session, SSH_FATAL,
385 		    "Error from fcntl(fd, F_SETFD, 1): %s",
386 		    strerror(errno));
387     close(fd);
388     return -1;
389   }
390 
391   if (connect(fd, (struct sockaddr *) &sunaddr,
392         sizeof(sunaddr)) < 0) {
393     ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
394 		    strerror(errno));
395     close(fd);
396     return -1;
397   }
398   ssh_socket_set_fd(s,fd);
399   return 0;
400 }
401 #endif
402 
403 /** \internal
404  * \brief closes a socket
405  */
406 void ssh_socket_close(ssh_socket s){
407   if (ssh_socket_is_open(s)) {
408 #ifdef _WIN32
409     closesocket(s->fd_in);
410     /* fd_in = fd_out under win32 */
411     s->last_errno = WSAGetLastError();
412 #else
413     close(s->fd_in);
414     if(s->fd_out != s->fd_in && s->fd_out != -1)
415       close(s->fd_out);
416     s->last_errno = errno;
417 #endif
418     s->fd_in = s->fd_out = SSH_INVALID_SOCKET;
419   }
420   if(s->poll_in != NULL){
421     if(s->poll_out == s->poll_in)
422       s->poll_out = NULL;
423     ssh_poll_free(s->poll_in);
424     s->poll_in=NULL;
425   }
426   if(s->poll_out != NULL){
427     ssh_poll_free(s->poll_out);
428     s->poll_out=NULL;
429   }
430 }
431 
432 /**
433  * @internal
434  * @brief sets the file descriptor of the socket.
435  * @param[out] s ssh_socket to update
436  * @param[in] fd file descriptor to set
437  * @warning this function updates boths the input and output
438  * file descriptors
439  */
440 void ssh_socket_set_fd(ssh_socket s, socket_t fd) {
441   s->fd_in = s->fd_out = fd;
442   if(s->poll_in)
443   	ssh_poll_set_fd(s->poll_in,fd);
444 }
445 
446 /**
447  * @internal
448  * @brief sets the input file descriptor of the socket.
449  * @param[out] s ssh_socket to update
450  * @param[in] fd file descriptor to set
451  */
452 void ssh_socket_set_fd_in(ssh_socket s, socket_t fd) {
453   s->fd_in = fd;
454   if(s->poll_in)
455     ssh_poll_set_fd(s->poll_in,fd);
456 }
457 
458 /**
459  * @internal
460  * @brief sets the output file descriptor of the socket.
461  * @param[out] s ssh_socket to update
462  * @param[in] fd file descriptor to set
463  */
464 void ssh_socket_set_fd_out(ssh_socket s, socket_t fd) {
465   s->fd_out = fd;
466   if(s->poll_out)
467     ssh_poll_set_fd(s->poll_out,fd);
468 }
469 
470 
471 
472 /** \internal
473  * \brief returns the input file descriptor of the socket
474  */
475 socket_t ssh_socket_get_fd_in(ssh_socket s) {
476   return s->fd_in;
477 }
478 
479 /** \internal
480  * \brief returns nonzero if the socket is open
481  */
482 int ssh_socket_is_open(ssh_socket s) {
483   return s->fd_in != SSH_INVALID_SOCKET;
484 }
485 
486 /** \internal
487  * \brief read len bytes from socket into buffer
488  */
489 static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len) {
490   int rc = -1;
491 
492   if (s->data_except) {
493     return -1;
494   }
495   if(s->fd_is_socket)
496     rc = recv(s->fd_in,buffer, len, 0);
497   else
498     rc = read(s->fd_in,buffer, len);
499 #ifdef _WIN32
500   s->last_errno = WSAGetLastError();
501 #else
502   s->last_errno = errno;
503 #endif
504   s->read_wontblock = 0;
505 
506   if (rc < 0) {
507     s->data_except = 1;
508   }
509 
510   return rc;
511 }
512 
513 /** \internal
514  * \brief writes len bytes from buffer to socket
515  */
516 static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer,
517     uint32_t len) {
518   int w = -1;
519 
520   if (s->data_except) {
521     return -1;
522   }
523   if (s->fd_is_socket)
524     w = send(s->fd_out,buffer, len, 0);
525   else
526     w = write(s->fd_out, buffer, len);
527 #ifdef _WIN32
528   s->last_errno = WSAGetLastError();
529 #else
530   s->last_errno = errno;
531 #endif
532   s->write_wontblock = 0;
533   /* Reactive the POLLOUT detector in the poll multiplexer system */
534   if(s->poll_out){
535   	ssh_log(s->session, SSH_LOG_PACKET, "Enabling POLLOUT for socket");
536   	ssh_poll_set_events(s->poll_out,ssh_poll_get_events(s->poll_out) | POLLOUT);
537   }
538   if (w < 0) {
539     s->data_except = 1;
540   }
541 
542   return w;
543 }
544 
545 /** \internal
546  * \brief returns nonzero if the current socket is in the fd_set
547  */
548 int ssh_socket_fd_isset(ssh_socket s, fd_set *set) {
549   if(s->fd_in == SSH_INVALID_SOCKET) {
550     return 0;
551   }
552   return FD_ISSET(s->fd_in,set) || FD_ISSET(s->fd_out,set);
553 }
554 
555 /** \internal
556  * \brief sets the current fd in a fd_set and updates the max_fd
557  */
558 
559 void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) {
560   if (s->fd_in == SSH_INVALID_SOCKET) {
561     return;
562   }
563 
564   FD_SET(s->fd_in,set);
565   FD_SET(s->fd_out,set);
566 
567   if (s->fd_in >= 0 &&
568       s->fd_in >= *max_fd &&
569       s->fd_in != SSH_INVALID_SOCKET) {
570       *max_fd = s->fd_in + 1;
571   }
572   if (s->fd_out >= 0 &&
573       s->fd_out >= *max_fd &&
574       s->fd_out != SSH_INVALID_SOCKET) {
575       *max_fd = s->fd_out + 1;
576   }
577 }
578 
579 /** \internal
580  * \brief buffered write of data
581  * \returns SSH_OK, or SSH_ERROR
582  * \warning has no effect on socket before a flush
583  */
584 int ssh_socket_write(ssh_socket s, const void *buffer, int len) {
585   ssh_session session = s->session;
586   enter_function();
587   if(len > 0) {
588     if (buffer_add_data(s->out_buffer, buffer, len) < 0) {
589       ssh_set_error_oom(s->session);
590       return SSH_ERROR;
591     }
592     ssh_socket_nonblocking_flush(s);
593   }
594   leave_function();
595   return SSH_OK;
596 }
597 
598 
599 /** \internal
600  * \brief starts a nonblocking flush of the output buffer
601  *
602  */
603 int ssh_socket_nonblocking_flush(ssh_socket s) {
604   ssh_session session = s->session;
605   uint32_t len;
606   int w;
607 
608   enter_function();
609 
610   if (!ssh_socket_is_open(s)) {
611     session->alive = 0;
612     /* FIXME use ssh_socket_get_errno */
613     ssh_set_error(session, SSH_FATAL,
614         "Writing packet: error on socket (or connection closed): %s",
615         strerror(s->last_errno));
616 
617     leave_function();
618     return SSH_ERROR;
619   }
620 
621   len = buffer_get_rest_len(s->out_buffer);
622   if (!s->write_wontblock && s->poll_out && len > 0) {
623       /* force the poll system to catch pollout events */
624       ssh_poll_add_events(s->poll_out, POLLOUT);
625       leave_function();
626       return SSH_AGAIN;
627   }
628   if (s->write_wontblock && len > 0) {
629     w = ssh_socket_unbuffered_write(s, buffer_get_rest(s->out_buffer), len);
630     if (w < 0) {
631       session->alive = 0;
632       ssh_socket_close(s);
633       /* FIXME use ssh_socket_get_errno() */
634       /* FIXME use callback for errors */
635       ssh_set_error(session, SSH_FATAL,
636           "Writing packet: error on socket (or connection closed): %s",
637           strerror(s->last_errno));
638       leave_function();
639       return SSH_ERROR;
640     }
641     buffer_pass_bytes(s->out_buffer, w);
642   }
643 
644   /* Is there some data pending? */
645   len = buffer_get_rest_len(s->out_buffer);
646   if (s->poll_out && len > 0) {
647       /* force the poll system to catch pollout events */
648       ssh_poll_add_events(s->poll_out, POLLOUT);
649       leave_function();
650       return SSH_AGAIN;
651   }
652 
653   /* all data written */
654   leave_function();
655   return SSH_OK;
656 }
657 
658 void ssh_socket_set_write_wontblock(ssh_socket s) {
659   s->write_wontblock = 1;
660 }
661 
662 void ssh_socket_set_read_wontblock(ssh_socket s) {
663   s->read_wontblock = 1;
664 }
665 
666 void ssh_socket_set_except(ssh_socket s) {
667   s->data_except = 1;
668 }
669 
670 int ssh_socket_data_available(ssh_socket s) {
671   return s->read_wontblock;
672 }
673 
674 int ssh_socket_data_writable(ssh_socket s) {
675   return s->write_wontblock;
676 }
677 
678 /** @internal
679  * @brief returns the number of outgoing bytes currently buffered
680  * @param s the socket
681  * @returns numbers of bytes buffered, or 0 if the socket isn't connected
682  */
683 int ssh_socket_buffered_write_bytes(ssh_socket s){
684 	if(s==NULL || s->out_buffer == NULL)
685 		return 0;
686 	return buffer_get_rest_len(s->out_buffer);
687 }
688 
689 
690 int ssh_socket_get_status(ssh_socket s) {
691   int r = 0;
692 
693   if (s->read_wontblock) {
694     r |= SSH_READ_PENDING;
695   }
696 
697   if (s->data_except) {
698     r |= SSH_CLOSED_ERROR;
699   }
700 
701   return r;
702 }
703 
704 #ifdef _WIN32
705 void ssh_socket_set_nonblocking(socket_t fd) {
706   u_long nonblocking = 1;
707   ioctlsocket(fd, FIONBIO, &nonblocking);
708 }
709 
710 void ssh_socket_set_blocking(socket_t fd) {
711   u_long nonblocking = 0;
712   ioctlsocket(fd, FIONBIO, &nonblocking);
713 }
714 
715 #else /* _WIN32 */
716 void ssh_socket_set_nonblocking(socket_t fd) {
717   fcntl(fd, F_SETFL, O_NONBLOCK);
718 }
719 
720 void ssh_socket_set_blocking(socket_t fd) {
721   fcntl(fd, F_SETFL, 0);
722 }
723 #endif /* _WIN32 */
724 
725 /**
726  * @internal
727  * @brief Launches a socket connection
728  * If a the socket connected callback has been defined and
729  * a poll object exists, this call will be non blocking.
730  * @param s    socket to connect.
731  * @param host hostname or ip address to connect to.
732  * @param port port number to connect to.
733  * @param bind_addr address to bind to, or NULL for default.
734  * @returns SSH_OK socket is being connected.
735  * @returns SSH_ERROR error while connecting to remote host.
736  * @bug It only tries connecting to one of the available AI's
737  * which is problematic for hosts having DNS fail-over.
738  */
739 
740 int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr){
741 	socket_t fd;
742 	ssh_session session=s->session;
743 	enter_function();
744 	if(s->state != SSH_SOCKET_NONE) {
745 		ssh_set_error(s->session, SSH_FATAL,
746 				"ssh_socket_connect called on socket not unconnected");
747 		return SSH_ERROR;
748 	}
749 	fd=ssh_connect_host_nonblocking(s->session,host,bind_addr,port);
750 	ssh_log(session,SSH_LOG_PROTOCOL,"Nonblocking connection socket: %d",fd);
751 	if(fd == SSH_INVALID_SOCKET)
752 		return SSH_ERROR;
753 	ssh_socket_set_fd(s,fd);
754 	s->state=SSH_SOCKET_CONNECTING;
755 	/* POLLOUT is the event to wait for in a nonblocking connect */
756 	ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLOUT);
757 #ifdef _WIN32
758 	ssh_poll_add_events(ssh_socket_get_poll_handle_in(s),POLLWRNORM);
759 #endif
760 	leave_function();
761 	return SSH_OK;
762 }
763 
764 #ifndef _WIN32
765 /**
766  * @internal
767  * @brief executes a command and redirect input and outputs
768  * @param command command to execute
769  * @param in input file descriptor
770  * @param out output file descriptor
771  */
772 void ssh_execute_command(const char *command, socket_t in, socket_t out){
773   const char *args[]={"/bin/sh","-c",command,NULL};
774   /* redirect in and out to stdin, stdout and stderr */
775   dup2(in, 0);
776   dup2(out,1);
777   dup2(out,2);
778   close(in);
779   close(out);
780   execv(args[0],(char * const *)args);
781   exit(1);
782 }
783 
784 /**
785  * @internal
786  * @brief Open a socket on a ProxyCommand
787  * This call will always be nonblocking.
788  * @param s    socket to connect.
789  * @param command Command to execute.
790  * @returns SSH_OK socket is being connected.
791  * @returns SSH_ERROR error while executing the command.
792  */
793 
794 int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){
795   socket_t in_pipe[2];
796   socket_t out_pipe[2];
797   int pid;
798   int rc;
799   ssh_session session=s->session;
800   enter_function();
801   if(s->state != SSH_SOCKET_NONE)
802     return SSH_ERROR;
803 
804   rc = pipe(in_pipe);
805   if (rc < 0) {
806       return SSH_ERROR;
807   }
808   rc = pipe(out_pipe);
809   if (rc < 0) {
810       return SSH_ERROR;
811   }
812 
813   ssh_log(session,SSH_LOG_PROTOCOL,"Executing proxycommand '%s'",command);
814   pid = fork();
815   if(pid == 0){
816     ssh_execute_command(command,out_pipe[0],in_pipe[1]);
817   }
818   close(in_pipe[1]);
819   close(out_pipe[0]);
820   ssh_log(session,SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",in_pipe[0],out_pipe[1]);
821   ssh_socket_set_fd_in(s,in_pipe[0]);
822   ssh_socket_set_fd_out(s,out_pipe[1]);
823   s->state=SSH_SOCKET_CONNECTED;
824   s->fd_is_socket=0;
825   /* POLLOUT is the event to wait for in a nonblocking connect */
826   ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLIN);
827   ssh_poll_set_events(ssh_socket_get_poll_handle_out(s),POLLOUT);
828   if(s->callbacks && s->callbacks->connected)
829     s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata);
830   leave_function();
831   return SSH_OK;
832 }
833 
834 #endif /* _WIN32 */
835 /** @} */
836 
837 /* vim: set ts=4 sw=4 et cindent: */
838