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