1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Sara Golemon <pollita@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24 #include "php_ssh2.h"
25
php_ssh2_zval_from_resource_handle(int handle)26 void *php_ssh2_zval_from_resource_handle(int handle) {
27 zval *val;
28 zend_resource *zr;
29 ZEND_HASH_FOREACH_VAL(&EG(regular_list), val) {
30 zr = Z_RES_P(val);
31 if (zr->handle == handle) {
32 return val;
33 }
34 } ZEND_HASH_FOREACH_END();
35 return NULL;
36 }
37
38
39 /* **********************
40 * channel_stream_ops *
41 ********************** */
42
43 #if PHP_VERSION_ID < 70400
php_ssh2_channel_stream_write(php_stream * stream,const char * buf,size_t count)44 static size_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count)
45 #else
46 static ssize_t php_ssh2_channel_stream_write(php_stream *stream, const char *buf, size_t count)
47 #endif
48 {
49 php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
50 ssize_t writestate;
51 LIBSSH2_SESSION *session;
52
53 libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking);
54 session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
55
56
57
58 #ifdef PHP_SSH2_SESSION_TIMEOUT
59 if (abstract->is_blocking) {
60 libssh2_session_set_timeout(session, abstract->timeout);
61 }
62 #endif
63
64 writestate = libssh2_channel_write_ex(abstract->channel, abstract->streamid, buf, count);
65
66 #ifdef PHP_SSH2_SESSION_TIMEOUT
67 if (abstract->is_blocking) {
68 libssh2_session_set_timeout(session, 0);
69 }
70 #endif
71
72 if (writestate == LIBSSH2_ERROR_EAGAIN) {
73 #if PHP_VERSION_ID < 70400
74 writestate = 0;
75 #endif
76 } else if (writestate < 0) {
77 char *error_msg = NULL;
78 if (libssh2_session_last_error(session, &error_msg, NULL, 0) == writestate) {
79 php_error_docref(NULL, E_WARNING, "Failure '%s' (%ld)", error_msg, writestate);
80 }
81
82 stream->eof = 1;
83 #if PHP_VERSION_ID < 70400
84 writestate = 0;
85 #endif
86 }
87
88 return writestate;
89 }
90
91 #if PHP_VERSION_ID < 70400
php_ssh2_channel_stream_read(php_stream * stream,char * buf,size_t count)92 static size_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count)
93 #else
94 static ssize_t php_ssh2_channel_stream_read(php_stream *stream, char *buf, size_t count)
95 #endif
96 {
97 php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
98 ssize_t readstate;
99 LIBSSH2_SESSION *session;
100
101 stream->eof = libssh2_channel_eof(abstract->channel);
102 libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking);
103 session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
104
105 #ifdef PHP_SSH2_SESSION_TIMEOUT
106 if (abstract->is_blocking) {
107 libssh2_session_set_timeout(session, abstract->timeout);
108 }
109 #endif
110
111 readstate = libssh2_channel_read_ex(abstract->channel, abstract->streamid, buf, count);
112
113 #ifdef PHP_SSH2_SESSION_TIMEOUT
114 if (abstract->is_blocking) {
115 libssh2_session_set_timeout(session, 0);
116 }
117 #endif
118
119 if (readstate == LIBSSH2_ERROR_EAGAIN) {
120 #if PHP_VERSION_ID < 70400
121 readstate = 0;
122 #endif
123 } else if (readstate < 0) {
124 char *error_msg = NULL;
125 if (libssh2_session_last_error(session, &error_msg, NULL, 0) == readstate) {
126 php_error_docref(NULL, E_WARNING, "Failure '%s' (%ld)", error_msg, readstate);
127 }
128
129 stream->eof = 1;
130 readstate = 0;
131 }
132 return readstate;
133 }
134
php_ssh2_channel_stream_close(php_stream * stream,int close_handle)135 static int php_ssh2_channel_stream_close(php_stream *stream, int close_handle)
136 {
137 php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
138
139 if (!abstract->refcount || (--(*(abstract->refcount)) == 0)) {
140 /* Last one out, turn off the lights */
141 if (abstract->refcount) {
142 efree(abstract->refcount);
143 }
144 libssh2_channel_eof(abstract->channel);
145 libssh2_channel_free(abstract->channel);
146 zend_list_delete(abstract->session_rsrc);
147 }
148 efree(abstract);
149
150 return 0;
151 }
152
php_ssh2_channel_stream_flush(php_stream * stream)153 static int php_ssh2_channel_stream_flush(php_stream *stream)
154 {
155 php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
156
157 return libssh2_channel_flush_ex(abstract->channel, abstract->streamid);
158 }
159
php_ssh2_channel_stream_cast(php_stream * stream,int castas,void ** ret)160 static int php_ssh2_channel_stream_cast(php_stream *stream, int castas, void **ret)
161 {
162 php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
163 LIBSSH2_SESSION *session;
164 php_ssh2_session_data **session_data;
165
166 session = (LIBSSH2_SESSION *)zend_fetch_resource(abstract->session_rsrc, PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
167 session_data = (php_ssh2_session_data **)libssh2_session_abstract(session);
168
169 switch (castas) {
170 case PHP_STREAM_AS_FD:
171 case PHP_STREAM_AS_FD_FOR_SELECT:
172 case PHP_STREAM_AS_SOCKETD:
173 if (ret) {
174 *(php_socket_t *)ret = (*session_data)->socket;
175 }
176 return SUCCESS;
177 default:
178 return FAILURE;
179 }
180 }
181
php_ssh2_channel_stream_set_option(php_stream * stream,int option,int value,void * ptrparam)182 static int php_ssh2_channel_stream_set_option(php_stream *stream, int option, int value, void *ptrparam)
183 {
184 php_ssh2_channel_data *abstract = (php_ssh2_channel_data*)stream->abstract;
185 int ret;
186
187 switch (option) {
188 case PHP_STREAM_OPTION_BLOCKING:
189 ret = abstract->is_blocking;
190 abstract->is_blocking = value;
191 return ret;
192 break;
193
194 case PHP_STREAM_OPTION_META_DATA_API:
195 add_assoc_long((zval*)ptrparam, "exit_status", libssh2_channel_get_exit_status(abstract->channel));
196 break;
197
198 case PHP_STREAM_OPTION_READ_TIMEOUT:
199 ret = abstract->timeout;
200 #ifdef PHP_SSH2_SESSION_TIMEOUT
201 struct timeval tv = *(struct timeval*)ptrparam;
202 abstract->timeout = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
203 #else
204 php_error_docref(NULL, E_WARNING, "No support for ssh2 stream timeout. Please recompile with libssh2 >= 1.2.9");
205 #endif
206 return ret;
207 break;
208
209 case PHP_STREAM_OPTION_CHECK_LIVENESS:
210 return stream->eof = libssh2_channel_eof(abstract->channel);
211 break;
212 }
213
214 return -1;
215 }
216
217 php_stream_ops php_ssh2_channel_stream_ops = {
218 php_ssh2_channel_stream_write,
219 php_ssh2_channel_stream_read,
220 php_ssh2_channel_stream_close,
221 php_ssh2_channel_stream_flush,
222 PHP_SSH2_CHANNEL_STREAM_NAME,
223 NULL, /* seek */
224 php_ssh2_channel_stream_cast,
225 NULL, /* stat */
226 php_ssh2_channel_stream_set_option,
227 };
228
229 /* *********************
230 * Magic Path Helper *
231 ********************* */
232
233 /* {{{ php_ssh2_fopen_wraper_parse_path
234 * Parse an ssh2.*:// path
235 */
php_ssh2_fopen_wraper_parse_path(const char * path,char * type,php_stream_context * context,LIBSSH2_SESSION ** psession,zend_resource ** presource,LIBSSH2_SFTP ** psftp,zend_resource ** psftp_rsrc)236 php_url *php_ssh2_fopen_wraper_parse_path(const char *path, char *type, php_stream_context *context,
237 LIBSSH2_SESSION **psession, zend_resource **presource,
238 LIBSSH2_SFTP **psftp, zend_resource **psftp_rsrc)
239 {
240 php_ssh2_sftp_data *sftp_data = NULL;
241 LIBSSH2_SESSION *session;
242 php_url *resource;
243 zval *methods = NULL, *callbacks = NULL, zsession, *tmpzval;
244 zend_long resource_id;
245 char *h, *username = NULL, *password = NULL, *pubkey_file = NULL, *privkey_file = NULL;
246 int username_len = 0, password_len = 0;
247
248 h = strstr(path, "Resource id #");
249 if (h) {
250 /* Starting with 5.6.28, 7.0.13 need to be clean, else php_url_parse will fail */
251 char *tmp = estrdup(path);
252
253 strncpy(tmp + (h-path), h + sizeof("Resource id #")-1, strlen(tmp)-sizeof("Resource id #"));
254 resource = php_url_parse(tmp);
255 efree(tmp);
256 } else {
257 resource = php_url_parse(path);
258 }
259 if (!resource || !resource->path) {
260 return NULL;
261 }
262
263 if (strncmp(SSH2_URL_STR(resource->scheme), "ssh2.", sizeof("ssh2.") - 1)) {
264 /* Not an ssh wrapper */
265 php_url_free(resource);
266 return NULL;
267 }
268
269 if (strcmp(SSH2_URL_STR(resource->scheme) + sizeof("ssh2.") - 1, type)) {
270 /* Wrong ssh2. wrapper type */
271 php_url_free(resource);
272 return NULL;
273 }
274
275 if (!resource->host) {
276 return NULL;
277 }
278
279 /*
280 Find resource->path in the path string, then copy the entire string from the original path.
281 This includes ?query#fragment in the path string
282 */
283 // TODO copy seems uneeded
284 #if PHP_VERSION_ID < 70300
285 {
286 char * s;
287
288 s = resource->path;
289 resource->path = estrdup(strstr(path, resource->path));
290 efree(s);
291 }
292 #else
293 {
294 zend_string *tmp;
295
296 tmp = resource->path;
297 resource->path = zend_string_init(ZSTR_VAL(resource->path), ZSTR_LEN(resource->path), 0);
298 zend_string_release(tmp);
299 }
300 #endif
301
302 /* Look for a resource ID to reuse a session */
303 if (is_numeric_string(SSH2_URL_STR(resource->host), SSH2_URL_LEN(resource->host), &resource_id, NULL, 0) == IS_LONG) {
304 php_ssh2_sftp_data *sftp_data;
305 zval *zresource;
306
307 if ((zresource = php_ssh2_zval_from_resource_handle(resource_id)) == NULL) {
308 php_url_free(resource);
309 return NULL;
310 }
311
312 if (psftp) {
313 /* suppress potential warning by passing NULL as resource_type_name */
314 sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(zresource), NULL, le_ssh2_sftp);
315 if (sftp_data) {
316 /* Want the sftp layer */
317 Z_ADDREF_P(zresource);
318 *psftp_rsrc = Z_RES_P(zresource);
319 *psftp = sftp_data->sftp;
320 *presource = sftp_data->session_rsrc;
321 *psession = sftp_data->session;
322 return resource;
323 }
324 }
325 session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zresource), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
326 if (session) {
327 if (psftp) {
328 /* We need an sftp layer too */
329 LIBSSH2_SFTP *sftp = libssh2_sftp_init(session);
330
331 if (!sftp) {
332 php_url_free(resource);
333 return NULL;
334 }
335 sftp_data = emalloc(sizeof(php_ssh2_sftp_data));
336 sftp_data->sftp = sftp;
337 sftp_data->session = session;
338 sftp_data->session_rsrc = Z_RES_P(zresource);
339 Z_ADDREF_P(zresource);
340 *psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp);
341 *psftp = sftp;
342 *presource = Z_RES_P(zresource);
343 *psession = session;
344 return resource;
345 }
346 Z_ADDREF_P(zresource);
347 *presource = Z_RES_P(zresource);
348 *psession = session;
349 return resource;
350 }
351 }
352
353 /* Fallback on finding it in the context */
354 if (SSH2_URL_STR(resource->host)[0] == 0 && context && psftp &&
355 (tmpzval = php_stream_context_get_option(context, "ssh2", "sftp")) != NULL &&
356 Z_TYPE_P(tmpzval) == IS_RESOURCE) {
357 php_ssh2_sftp_data *sftp_data;
358 sftp_data = (php_ssh2_sftp_data *)zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SFTP_RES_NAME, le_ssh2_sftp);
359 if (sftp_data) {
360 Z_ADDREF_P(tmpzval);
361 *psftp_rsrc = Z_RES_P(tmpzval);
362 *psftp = sftp_data->sftp;
363 *presource = sftp_data->session_rsrc;
364 *psession = sftp_data->session;
365 return resource;
366 }
367 }
368 if (SSH2_URL_STR(resource->host)[0] == 0 && context &&
369 (tmpzval = php_stream_context_get_option(context, "ssh2", "session")) != NULL &&
370 Z_TYPE_P(tmpzval) == IS_RESOURCE) {
371 session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(tmpzval), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session);
372 if (session) {
373 if (psftp) {
374 /* We need an SFTP layer too! */
375 LIBSSH2_SFTP *sftp = libssh2_sftp_init(session);
376 php_ssh2_sftp_data *sftp_data;
377
378 if (!sftp) {
379 php_url_free(resource);
380 return NULL;
381 }
382 sftp_data = emalloc(sizeof(php_ssh2_sftp_data));
383 sftp_data->sftp = sftp;
384 sftp_data->session = session;
385 sftp_data->session_rsrc = Z_RES_P(tmpzval);
386 Z_ADDREF_P(tmpzval);
387 *psftp_rsrc = zend_register_resource(sftp_data, le_ssh2_sftp);
388 *psftp = sftp;
389 *presource = Z_RES_P(tmpzval);
390 *psession = session;
391 return resource;
392 }
393 Z_ADDREF_P(tmpzval);
394 *psession = session;
395 *presource = Z_RES_P(tmpzval);
396 return resource;
397 }
398 }
399
400 /* Make our own connection then */
401 if (!resource->port) {
402 resource->port = 22;
403 }
404
405 if (context &&
406 (tmpzval = php_stream_context_get_option(context, "ssh2", "methods")) != NULL &&
407 Z_TYPE_P(tmpzval) == IS_ARRAY) {
408 methods = tmpzval;
409 }
410
411 if (context &&
412 (tmpzval = php_stream_context_get_option(context, "ssh2", "callbacks")) != NULL &&
413 Z_TYPE_P(tmpzval) == IS_ARRAY) {
414 callbacks = tmpzval;
415 }
416
417 if (context &&
418 (tmpzval = php_stream_context_get_option(context, "ssh2", "username")) != NULL &&
419 Z_TYPE_P(tmpzval) == IS_STRING) {
420 username = Z_STRVAL_P(tmpzval);
421 username_len = Z_STRLEN_P(tmpzval);
422 }
423
424 if (context &&
425 (tmpzval = php_stream_context_get_option(context, "ssh2", "password")) != NULL &&
426 Z_TYPE_P(tmpzval) == IS_STRING) {
427 password = Z_STRVAL_P(tmpzval);
428 password_len = Z_STRLEN_P(tmpzval);
429 }
430
431 if (context &&
432 (tmpzval = php_stream_context_get_option(context, "ssh2", "pubkey_file")) != NULL &&
433 Z_TYPE_P(tmpzval) == IS_STRING) {
434 pubkey_file = Z_STRVAL_P(tmpzval);
435 }
436
437 if (context &&
438 (tmpzval = php_stream_context_get_option(context, "ssh2", "privkey_file")) != NULL &&
439 Z_TYPE_P(tmpzval) == IS_STRING) {
440 privkey_file = Z_STRVAL_P(tmpzval);
441 }
442
443 if (resource->user) {
444 int len = SSH2_URL_LEN(resource->user);
445
446 if (len) {
447 username = SSH2_URL_STR(resource->user);
448 username_len = len;
449 }
450 }
451
452 if (resource->pass) {
453 int len = SSH2_URL_LEN(resource->pass);
454
455 if (len) {
456 password = SSH2_URL_STR(resource->pass);
457 password_len = len;
458 }
459 }
460
461 if (!username) {
462 /* username is a minimum */
463 php_url_free(resource);
464 return NULL;
465 }
466
467 session = php_ssh2_session_connect(SSH2_URL_STR(resource->host), resource->port, methods, callbacks);
468 if (!session) {
469 /* Unable to connect! */
470 php_url_free(resource);
471 return NULL;
472 }
473
474 /* Authenticate */
475 if (pubkey_file && privkey_file) {
476 if (php_check_open_basedir(pubkey_file) || php_check_open_basedir(privkey_file)) {
477 php_url_free(resource);
478 return NULL;
479 }
480
481 /* Attempt pubkey authentication */
482 if (!libssh2_userauth_publickey_fromfile(session, username, pubkey_file, privkey_file, password)) {
483 goto session_authed;
484 }
485 }
486
487 if (password) {
488 /* Attempt password authentication */
489 if (libssh2_userauth_password_ex(session, username, username_len, password, password_len, NULL) == 0) {
490 goto session_authed;
491 }
492 }
493
494 /* Auth failure */
495 php_url_free(resource);
496 if (Z_RES(zsession)) {
497 zend_list_delete(Z_RES(zsession));
498 }
499 return NULL;
500
501 session_authed:
502 ZVAL_RES(&zsession, zend_register_resource(session, le_ssh2_session));
503
504 if (psftp) {
505 LIBSSH2_SFTP *sftp;
506 zval zsftp;
507
508 sftp = libssh2_sftp_init(session);
509 if (!sftp) {
510 php_url_free(resource);
511 zend_list_delete(Z_RES(zsession));
512 return NULL;
513 }
514
515 sftp_data = emalloc(sizeof(php_ssh2_sftp_data));
516 sftp_data->session = session;
517 sftp_data->sftp = sftp;
518 sftp_data->session_rsrc = Z_RES(zsession);
519
520 //TODO Sean-Der
521 //ZEND_REGISTER_RESOURCE(sftp_data, le_ssh2_sftp);
522 *psftp_rsrc = Z_RES(zsftp);
523 *psftp = sftp;
524 }
525
526 *presource = Z_RES(zsession);
527 *psession = session;
528
529 return resource;
530 }
531 /* }}} */
532
533 /* *****************
534 * Shell Wrapper *
535 ***************** */
536
537 /* {{{ php_ssh2_shell_open
538 * Make a stream from a session
539 */
php_ssh2_shell_open(LIBSSH2_SESSION * session,zend_resource * resource,char * term,int term_len,zval * environment,long width,long height,long type)540 static php_stream *php_ssh2_shell_open(LIBSSH2_SESSION *session, zend_resource *resource, char *term, int term_len, zval *environment, long width, long height, long type)
541 {
542 LIBSSH2_CHANNEL *channel;
543 php_ssh2_channel_data *channel_data;
544 php_stream *stream;
545
546 libssh2_session_set_blocking(session, 1);
547
548 channel = libssh2_channel_open_session(session);
549 if (!channel) {
550 php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host");
551 return NULL;
552 }
553
554 if (environment) {
555 zend_string *key;
556 int key_type;
557 zend_ulong idx;
558
559 for(zend_hash_internal_pointer_reset(HASH_OF(environment));
560 (key_type = zend_hash_get_current_key(HASH_OF(environment), &key, &idx)) != HASH_KEY_NON_EXISTENT;
561 zend_hash_move_forward(HASH_OF(environment))) {
562 if (key_type == HASH_KEY_IS_STRING) {
563 zval *value;
564
565 if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) {
566 zval copyval = *value;
567
568 zval_copy_ctor(©val);
569 convert_to_string(©val);
570 if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) {
571 php_error_docref(NULL, E_WARNING, "Failed setting %s=%s on remote end", ZSTR_VAL(key), Z_STRVAL(copyval));
572 }
573 zval_dtor(©val);
574 }
575 } else {
576 php_error_docref(NULL, E_NOTICE, "Skipping numeric index in environment array");
577 }
578 }
579 }
580
581 if (type == PHP_SSH2_TERM_UNIT_CHARS) {
582 if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) {
583 php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height);
584 libssh2_channel_free(channel);
585 return NULL;
586 }
587 } else {
588 if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) {
589 php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height);
590 libssh2_channel_free(channel);
591 return NULL;
592 }
593 }
594
595 if (libssh2_channel_shell(channel)) {
596 php_error_docref(NULL, E_WARNING, "Unable to request shell from remote host");
597 libssh2_channel_free(channel);
598 return NULL;
599 }
600
601 /* Turn it into a stream */
602 channel_data = emalloc(sizeof(php_ssh2_channel_data));
603 channel_data->channel = channel;
604 channel_data->streamid = 0;
605 channel_data->is_blocking = 0;
606 channel_data->timeout = 0;
607 channel_data->session_rsrc = resource;
608 channel_data->refcount = NULL;
609
610 stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+");
611
612 return stream;
613 }
614 /* }}} */
615
616 /* {{{ php_ssh2_fopen_wrapper_shell
617 * ssh2.shell:// fopen wrapper
618 */
php_ssh2_fopen_wrapper_shell(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)619 static php_stream *php_ssh2_fopen_wrapper_shell(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
620 {
621 LIBSSH2_SESSION *session = NULL;
622 php_stream *stream;
623 zval *tmpzval, *environment = NULL;
624 char *terminal = PHP_SSH2_DEFAULT_TERMINAL;
625 zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;
626 zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;
627 zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;
628 zend_resource *rsrc = NULL;
629 int terminal_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1;
630 php_url *resource;
631 char *s;
632
633 resource = php_ssh2_fopen_wraper_parse_path(path, "shell", context, &session, &rsrc, NULL, NULL);
634 if (!resource || !session) {
635 return NULL;
636 }
637
638 if (context &&
639 (tmpzval = php_stream_context_get_option(context, "ssh2", "env")) != NULL && Z_TYPE_P(tmpzval) == IS_ARRAY) {
640 environment = tmpzval;
641 }
642
643 if (context &&
644 (tmpzval = php_stream_context_get_option(context, "ssh2", "term")) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) {
645 terminal = Z_STRVAL_P(tmpzval);
646 terminal_len = Z_STRLEN_P(tmpzval);
647 }
648
649 if (context &&
650 (tmpzval = php_stream_context_get_option(context, "ssh2", "term_width")) != NULL) {
651 zval copyval;
652 copyval = *tmpzval;
653 convert_to_long(©val);
654 width = Z_LVAL_P(©val);
655 zval_ptr_dtor(©val);
656 }
657
658 if (context &&
659 (tmpzval = php_stream_context_get_option(context, "ssh2", "term_height")) != NULL) {
660 zval copyval;
661 copyval = *tmpzval;
662 convert_to_long(©val);
663 height = Z_LVAL_P(©val);
664 zval_ptr_dtor(©val);
665 }
666
667 if (context &&
668 (tmpzval = php_stream_context_get_option(context, "ssh2", "term_units")) != NULL) {
669 zval copyval;
670 copyval = *tmpzval;
671 convert_to_long(©val);
672 type = Z_LVAL_P(©val);
673 zval_ptr_dtor(©val);
674 }
675
676 s = resource->path ? SSH2_URL_STR(resource->path) : NULL;
677
678 if (s && s[0] == '/') {
679 /* Terminal type encoded into URL overrides context terminal type */
680 char *p;
681
682 s++;
683 p = strchr(s, '/');
684 if (p) {
685 if (p - s) {
686 terminal = s;
687 terminal_len = p - terminal;
688 s += terminal_len + 1;
689 } else {
690 /* "null" terminal given, skip it */
691 s++;
692 }
693 } else {
694 int len;
695
696 if ((len = strlen(path + 1))) {
697 terminal = s;
698 terminal_len = len;
699 s += len;
700 }
701 }
702 }
703
704 /* TODO: Accept resolution and environment vars as URL style parameters
705 * ssh2.shell://hostorresource/terminal/99x99c?envvar=envval&envvar=envval....
706 */
707 stream = php_ssh2_shell_open(session, rsrc, terminal, terminal_len, environment, width, height, type);
708 if (!stream) {
709 zend_list_delete(rsrc);
710 }
711 php_url_free(resource);
712
713 return stream;
714 }
715 /* }}} */
716
717 static php_stream_wrapper_ops php_ssh2_shell_stream_wops = {
718 php_ssh2_fopen_wrapper_shell,
719 NULL, /* stream_close */
720 NULL, /* stat */
721 NULL, /* stat_url */
722 NULL, /* opendir */
723 "ssh2.shell"
724 };
725
726 php_stream_wrapper php_ssh2_stream_wrapper_shell = {
727 &php_ssh2_shell_stream_wops,
728 NULL,
729 0
730 };
731
732 /* {{{ proto stream ssh2_shell(resource session[, string term_type[, array env[, int width, int height[, int width_height_type]]]])
733 * Open a shell at the remote end and allocate a channel for it
734 */
PHP_FUNCTION(ssh2_shell)735 PHP_FUNCTION(ssh2_shell)
736 {
737 LIBSSH2_SESSION *session;
738 php_stream *stream;
739 zval *zsession;
740 zval *environment = NULL;
741 char *term = PHP_SSH2_DEFAULT_TERMINAL;
742 size_t term_len = sizeof(PHP_SSH2_DEFAULT_TERMINAL) - 1;
743 zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;
744 zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;
745 zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;
746 int argc = ZEND_NUM_ARGS();
747
748 if (argc == 5) {
749 php_error_docref(NULL, E_ERROR, "width specified without height parameter");
750 RETURN_FALSE;
751 }
752
753 if (zend_parse_parameters(argc, "r|sa!lll", &zsession, &term, &term_len, &environment, &width, &height, &type) == FAILURE) {
754 return;
755 }
756
757 SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
758
759 stream = php_ssh2_shell_open(session, Z_RES_P(zsession), term, term_len, environment, width, height, type);
760 if (!stream) {
761 RETURN_FALSE;
762 }
763
764 /* Ensure that channels are freed BEFORE the sessions they belong to */
765 Z_ADDREF_P(zsession);
766
767 php_stream_to_zval(stream, return_value);
768 }
769 /* }}} */
770
771 /* ****************
772 * Exec Wrapper *
773 **************** */
774
775 /* {{{ php_ssh2_exec_command
776 * Make a stream from a session
777 */
php_ssh2_exec_command(LIBSSH2_SESSION * session,zend_resource * rsrc,char * command,char * term,int term_len,zval * environment,long width,long height,long type)778 static php_stream *php_ssh2_exec_command(LIBSSH2_SESSION *session, zend_resource *rsrc, char *command, char *term, int term_len, zval *environment, long width, long height, long type)
779 {
780 LIBSSH2_CHANNEL *channel;
781 php_ssh2_channel_data *channel_data;
782 php_stream *stream;
783
784 libssh2_session_set_blocking(session, 1);
785
786 channel = libssh2_channel_open_session(session);
787 if (!channel) {
788 php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host");
789 return NULL;
790 }
791
792 if (environment) {
793 zend_string *key = NULL;
794 int key_type;
795 zend_ulong idx = 0;
796 HashPosition pos;
797
798 for(zend_hash_internal_pointer_reset_ex(HASH_OF(environment), &pos);
799 (key_type = zend_hash_get_current_key_ex(HASH_OF(environment), &key, &idx, &pos)) != HASH_KEY_NON_EXISTENT;
800 zend_hash_move_forward_ex(HASH_OF(environment), &pos)) {
801 if (key_type == HASH_KEY_IS_STRING) {
802 zval *value;
803
804 if ((value = zend_hash_get_current_data(HASH_OF(environment))) != NULL) {
805 zval copyval = *value;
806
807 zval_copy_ctor(©val);
808 convert_to_string(©val);
809 if (libssh2_channel_setenv_ex(channel, key->val, key->len, Z_STRVAL(copyval), Z_STRLEN(copyval))) {
810 php_error_docref(NULL, E_WARNING, "Failed setting %s=%s on remote end", ZSTR_VAL(key), Z_STRVAL(copyval));
811 }
812 zval_dtor(©val);
813 }
814 } else {
815 php_error_docref(NULL, E_NOTICE, "Skipping numeric index in environment array");
816 }
817 }
818 }
819
820 if (term) {
821 if (type == PHP_SSH2_TERM_UNIT_CHARS) {
822 if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, width, height, 0, 0)) {
823 php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld characters", term, width, height);
824 libssh2_channel_free(channel);
825 return NULL;
826 }
827 } else {
828 if (libssh2_channel_request_pty_ex(channel, term, term_len, NULL, 0, 0, 0, width, height)) {
829 php_error_docref(NULL, E_WARNING, "Failed allocating %s pty at %ldx%ld pixels", term, width, height);
830 libssh2_channel_free(channel);
831 return NULL;
832 }
833 }
834 }
835
836 if (libssh2_channel_exec(channel, command)) {
837 php_error_docref(NULL, E_WARNING, "Unable to request command execution on remote host");
838 libssh2_channel_free(channel);
839 return NULL;
840 }
841
842 /* Turn it into a stream */
843 channel_data = emalloc(sizeof(php_ssh2_channel_data));
844 channel_data->channel = channel;
845 channel_data->streamid = 0;
846 channel_data->is_blocking = 0;
847 channel_data->timeout = 0;
848 channel_data->session_rsrc = rsrc;
849 channel_data->refcount = NULL;
850
851 stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+");
852
853 return stream;
854 }
855 /* }}} */
856
857 /* {{{ php_ssh2_fopen_wrapper_exec
858 * ssh2.exec:// fopen wrapper
859 */
php_ssh2_fopen_wrapper_exec(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)860 static php_stream *php_ssh2_fopen_wrapper_exec(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
861 {
862 LIBSSH2_SESSION *session = NULL;
863 php_stream *stream;
864 zval *tmpzval, *environment = NULL;
865 zend_resource *rsrc = NULL;
866 php_url *resource;
867 char *terminal = NULL;
868 int terminal_len = 0;
869 long width = PHP_SSH2_DEFAULT_TERM_WIDTH;
870 long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;
871 long type = PHP_SSH2_DEFAULT_TERM_UNIT;
872
873 resource = php_ssh2_fopen_wraper_parse_path(path, "exec", context, &session, &rsrc, NULL, NULL);
874 if (!resource || !session) {
875 return NULL;
876 }
877 if (!resource->path) {
878 php_url_free(resource);
879 zend_list_delete(rsrc);
880 return NULL;
881 }
882
883 if (context &&
884 (tmpzval = php_stream_context_get_option(context, "ssh2", "env")) != NULL && Z_TYPE_P(tmpzval) == IS_ARRAY) {
885 environment = tmpzval;
886 }
887
888 if (context &&
889 (tmpzval = php_stream_context_get_option(context, "ssh2", "term")) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) {
890 terminal = Z_STRVAL_P(tmpzval);
891 terminal_len = Z_STRLEN_P(tmpzval);
892 }
893
894 if (context &&
895 (tmpzval = php_stream_context_get_option(context, "ssh2", "term_width")) != NULL) {
896 zval copyval;
897 copyval = *tmpzval;
898 convert_to_long(©val);
899 width = Z_LVAL_P(©val);
900 zval_ptr_dtor(©val);
901 }
902
903 if (context &&
904 (tmpzval = php_stream_context_get_option(context, "ssh2", "term_height")) != NULL) {
905 zval copyval;
906 copyval = *tmpzval;
907 convert_to_long(©val);
908 height = Z_LVAL_P(©val);
909 zval_ptr_dtor(©val);
910 }
911
912 if (context &&
913 (tmpzval = php_stream_context_get_option(context, "ssh2", "term_units")) != NULL) {
914 zval *copyval;
915 copyval = tmpzval;
916 convert_to_long(copyval);
917 type = Z_LVAL_P(copyval);
918 zval_ptr_dtor(copyval);
919 }
920
921 stream = php_ssh2_exec_command(session, rsrc, SSH2_URL_STR(resource->path) + 1, terminal, terminal_len, environment, width, height, type);
922 if (!stream) {
923 zend_list_delete(rsrc);
924 }
925 php_url_free(resource);
926
927 return stream;
928 }
929 /* }}} */
930
931 static php_stream_wrapper_ops php_ssh2_exec_stream_wops = {
932 php_ssh2_fopen_wrapper_exec,
933 NULL, /* stream_close */
934 NULL, /* stat */
935 NULL, /* stat_url */
936 NULL, /* opendir */
937 "ssh2.exec"
938 };
939
940 php_stream_wrapper php_ssh2_stream_wrapper_exec = {
941 &php_ssh2_exec_stream_wops,
942 NULL,
943 0
944 };
945
946 /* {{{ proto stream ssh2_exec(resource session, string command[, string pty[, array env[, int width[, int height[, int width_height_type]]]]])
947 * Execute a command at the remote end and allocate a channel for it
948 *
949 * This function has a dirty little secret.... pty and env can be in either order.... shhhh... don't tell anyone
950 */
PHP_FUNCTION(ssh2_exec)951 PHP_FUNCTION(ssh2_exec)
952 {
953 LIBSSH2_SESSION *session;
954 php_stream *stream;
955 zval *zsession;
956 zval *environment = NULL;
957 zval *zpty = NULL;
958 char *command;
959 size_t command_len;
960 zend_long width = PHP_SSH2_DEFAULT_TERM_WIDTH;
961 zend_long height = PHP_SSH2_DEFAULT_TERM_HEIGHT;
962 zend_long type = PHP_SSH2_DEFAULT_TERM_UNIT;
963 char *term = NULL;
964 int term_len = 0;
965
966 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|z!z!lll", &zsession, &command, &command_len, &zpty, &environment, &width, &height, &type) == FAILURE) {
967 return;
968 }
969
970 if (zpty && Z_TYPE_P(zpty) == IS_ARRAY) {
971 /* Swap pty and environment -- old call style */
972 zval *tmp = zpty;
973 zpty = environment;
974 environment = tmp;
975 }
976
977 if (environment && Z_TYPE_P(environment) != IS_ARRAY) {
978 php_error_docref(NULL, E_WARNING, "ssh2_exec() expects arg 4 to be of type array");
979 RETURN_FALSE;
980 }
981
982 if (zpty) {
983 convert_to_string(zpty);
984 term = Z_STRVAL_P(zpty);
985 term_len = Z_STRLEN_P(zpty);
986 }
987
988 SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
989
990 stream = php_ssh2_exec_command(session, Z_RES_P(zsession), command, term, term_len, environment, width, height, type);
991 if (!stream) {
992 RETURN_FALSE;
993 }
994
995 /* Ensure that channels are freed BEFORE the sessions they belong to */
996 Z_ADDREF_P(zsession);
997
998 php_stream_to_zval(stream, return_value);
999 }
1000 /* }}} */
1001
1002 /* ***************
1003 * SCP Wrapper *
1004 *************** */
1005
1006 /* {{{ php_ssh2_scp_xfer
1007 * Make a stream from a session
1008 */
php_ssh2_scp_xfer(LIBSSH2_SESSION * session,zend_resource * rsrc,char * filename)1009 static php_stream *php_ssh2_scp_xfer(LIBSSH2_SESSION *session, zend_resource *rsrc, char *filename)
1010 {
1011 LIBSSH2_CHANNEL *channel;
1012 php_ssh2_channel_data *channel_data;
1013 php_stream *stream;
1014
1015 channel = libssh2_scp_recv(session, filename, NULL);
1016 if (!channel) {
1017 char *error = "";
1018 libssh2_session_last_error(session, &error, NULL, 0);
1019 php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host: %s", error);
1020 return NULL;
1021 }
1022
1023 /* Turn it into a stream */
1024 channel_data = emalloc(sizeof(php_ssh2_channel_data));
1025 channel_data->channel = channel;
1026 channel_data->streamid = 0;
1027 channel_data->is_blocking = 0;
1028 channel_data->timeout = 0;
1029 channel_data->session_rsrc = rsrc;
1030 channel_data->refcount = NULL;
1031
1032 stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r");
1033
1034 return stream;
1035 }
1036 /* }}} */
1037
1038 /* {{{ php_ssh2_fopen_wrapper_scp
1039 * ssh2.scp:// fopen wrapper (Read mode only, if you want to know why write mode isn't supported as a stream, take a look at the SCP protocol)
1040 */
php_ssh2_fopen_wrapper_scp(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)1041 static php_stream *php_ssh2_fopen_wrapper_scp(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
1042 {
1043 LIBSSH2_SESSION *session = NULL;
1044 php_stream *stream;
1045 zend_resource *rsrc = NULL;
1046 php_url *resource;
1047
1048 if (strchr(mode, '+') || strchr(mode, 'a') || strchr(mode, 'w')) {
1049 return NULL;
1050 }
1051
1052 resource = php_ssh2_fopen_wraper_parse_path(path, "scp", context, &session, &rsrc, NULL, NULL);
1053 if (!resource || !session) {
1054 return NULL;
1055 }
1056 if (!resource->path) {
1057 php_url_free(resource);
1058 zend_list_delete(rsrc);
1059 return NULL;
1060 }
1061
1062 stream = php_ssh2_scp_xfer(session, rsrc, SSH2_URL_STR(resource->path));
1063 if (!stream) {
1064 zend_list_delete(rsrc);
1065 }
1066 php_url_free(resource);
1067
1068 return stream;
1069 }
1070 /* }}} */
1071
1072 static php_stream_wrapper_ops php_ssh2_scp_stream_wops = {
1073 php_ssh2_fopen_wrapper_scp,
1074 NULL, /* stream_close */
1075 NULL, /* stat */
1076 NULL, /* stat_url */
1077 NULL, /* opendir */
1078 "ssh2.scp"
1079 };
1080
1081 php_stream_wrapper php_ssh2_stream_wrapper_scp = {
1082 &php_ssh2_scp_stream_wops,
1083 NULL,
1084 0
1085 };
1086
1087 /* {{{ proto bool ssh2_scp_recv(resource session, string remote_file, string local_file)
1088 * Request a file via SCP
1089 */
PHP_FUNCTION(ssh2_scp_recv)1090 PHP_FUNCTION(ssh2_scp_recv)
1091 {
1092 LIBSSH2_SESSION *session;
1093 LIBSSH2_CHANNEL *remote_file;
1094 struct stat sb;
1095 php_stream *local_file;
1096 zval *zsession;
1097 char *remote_filename, *local_filename;
1098 size_t remote_filename_len, local_filename_len;
1099
1100 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &zsession, &remote_filename, &remote_filename_len,
1101 &local_filename, &local_filename_len) == FAILURE) {
1102 return;
1103 }
1104
1105 SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
1106
1107 remote_file = libssh2_scp_recv(session, remote_filename, &sb);
1108 if (!remote_file) {
1109 php_error_docref(NULL, E_WARNING, "Unable to receive remote file");
1110 RETURN_FALSE;
1111 }
1112 libssh2_channel_set_blocking(remote_file, 1);
1113
1114 local_file = php_stream_open_wrapper(local_filename, "wb", REPORT_ERRORS, NULL);
1115 if (!local_file) {
1116 php_error_docref(NULL, E_WARNING, "Unable to write to local file");
1117 libssh2_channel_free(remote_file);
1118 RETURN_FALSE;
1119 }
1120
1121 while (sb.st_size) {
1122 char buffer[8192];
1123 int bytes_read;
1124
1125 bytes_read = libssh2_channel_read(remote_file, buffer, sb.st_size > 8192 ? 8192 : sb.st_size);
1126 if (bytes_read < 0) {
1127 php_error_docref(NULL, E_WARNING, "Error reading from remote file");
1128 libssh2_channel_free(remote_file);
1129 php_stream_close(local_file);
1130 RETURN_FALSE;
1131 }
1132 php_stream_write(local_file, buffer, bytes_read);
1133 sb.st_size -= bytes_read;
1134 }
1135
1136 libssh2_channel_free(remote_file);
1137 php_stream_close(local_file);
1138
1139 RETURN_TRUE;
1140 }
1141 /* }}} */
1142
1143 /* {{{ proto stream ssh2_scp_send(resource session, string local_file, string remote_file[, int create_mode = 0644])
1144 * Send a file via SCP
1145 */
PHP_FUNCTION(ssh2_scp_send)1146 PHP_FUNCTION(ssh2_scp_send)
1147 {
1148 LIBSSH2_SESSION *session;
1149 LIBSSH2_CHANNEL *remote_file;
1150 php_stream *local_file;
1151 zval *zsession;
1152 char *local_filename, *remote_filename;
1153 size_t local_filename_len, remote_filename_len;
1154 zend_long create_mode = 0644;
1155 php_stream_statbuf ssb;
1156 int argc = ZEND_NUM_ARGS();
1157
1158 if (zend_parse_parameters(argc, "rss|l", &zsession, &local_filename, &local_filename_len,
1159 &remote_filename, &remote_filename_len, &create_mode) == FAILURE) {
1160 return;
1161 }
1162
1163 SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
1164
1165 local_file = php_stream_open_wrapper(local_filename, "rb", REPORT_ERRORS, NULL);
1166 if (!local_file) {
1167 php_error_docref(NULL, E_WARNING, "Unable to read source file");
1168 RETURN_FALSE;
1169 }
1170
1171 if (php_stream_stat(local_file, &ssb)) {
1172 php_error_docref(NULL, E_WARNING, "Failed statting local file");
1173 php_stream_close(local_file);
1174 RETURN_FALSE;
1175 }
1176
1177 if (argc < 4) {
1178 create_mode = ssb.sb.st_mode & 0777;
1179 }
1180
1181 remote_file = libssh2_scp_send_ex(session, remote_filename, create_mode, ssb.sb.st_size, ssb.sb.st_atime, ssb.sb.st_mtime);
1182 if (!remote_file) {
1183 int last_error = 0;
1184 char *error_msg = NULL;
1185
1186 last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);
1187 php_error_docref(NULL, E_WARNING, "Failure creating remote file: %s (%d)", error_msg, last_error);
1188 php_stream_close(local_file);
1189 RETURN_FALSE;
1190 }
1191 libssh2_channel_set_blocking(remote_file, 1);
1192
1193 while (ssb.sb.st_size) {
1194 char buffer[8192];
1195 size_t toread = MIN(8192, ssb.sb.st_size);
1196 size_t bytesread = php_stream_read(local_file, buffer, toread);
1197 size_t sent = 0;
1198 size_t justsent = 0;
1199
1200 if (bytesread <= 0 || bytesread > toread) {
1201 php_error_docref(NULL, E_WARNING, "Failed copying file 2");
1202 php_stream_close(local_file);
1203 libssh2_channel_free(remote_file);
1204 RETURN_FALSE;
1205 }
1206
1207
1208 while (bytesread - sent > 0) {
1209 if ((justsent = libssh2_channel_write(remote_file, (buffer + sent), bytesread - sent)) < 0) {
1210
1211 switch (justsent) {
1212 case LIBSSH2_ERROR_EAGAIN:
1213 php_error_docref(NULL, E_WARNING, "Operation would block");
1214 break;
1215
1216 case LIBSSH2_ERROR_ALLOC:
1217 php_error_docref(NULL,E_WARNING, "An internal memory allocation call failed");
1218 break;
1219
1220 case LIBSSH2_ERROR_SOCKET_SEND:
1221 php_error_docref(NULL,E_WARNING, "Unable to send data on socket");
1222 break;
1223
1224 case LIBSSH2_ERROR_CHANNEL_CLOSED:
1225 php_error_docref(NULL,E_WARNING, "The channel has been closed");
1226 break;
1227
1228 case LIBSSH2_ERROR_CHANNEL_EOF_SENT:
1229 php_error_docref(NULL,E_WARNING, "The channel has been requested to be closed");
1230 break;
1231 }
1232
1233 php_stream_close(local_file);
1234 libssh2_channel_free(remote_file);
1235 RETURN_FALSE;
1236 }
1237 sent = sent + justsent;
1238 }
1239 ssb.sb.st_size -= bytesread;
1240 }
1241
1242 libssh2_channel_flush_ex(remote_file, LIBSSH2_CHANNEL_FLUSH_ALL);
1243 php_stream_close(local_file);
1244 libssh2_channel_free(remote_file);
1245 RETURN_TRUE;
1246 }
1247 /* }}} */
1248
1249 /* ***************************
1250 * Direct TCP/IP Transport *
1251 *************************** */
1252
1253 /* {{{ php_ssh2_direct_tcpip
1254 * Make a stream from a session
1255 */
php_ssh2_direct_tcpip(LIBSSH2_SESSION * session,zend_resource * rsrc,char * host,int port)1256 static php_stream *php_ssh2_direct_tcpip(LIBSSH2_SESSION *session, zend_resource *rsrc, char *host, int port)
1257 {
1258 LIBSSH2_CHANNEL *channel;
1259 php_ssh2_channel_data *channel_data;
1260 php_stream *stream;
1261
1262 libssh2_session_set_blocking(session, 1);
1263
1264 channel = libssh2_channel_direct_tcpip(session, host, port);
1265 if (!channel) {
1266 php_error_docref(NULL, E_WARNING, "Unable to request a channel from remote host");
1267 return NULL;
1268 }
1269
1270 /* Turn it into a stream */
1271 channel_data = emalloc(sizeof(php_ssh2_channel_data));
1272 channel_data->channel = channel;
1273 channel_data->streamid = 0;
1274 channel_data->is_blocking = 0;
1275 channel_data->timeout = 0;
1276 channel_data->session_rsrc = rsrc;
1277 channel_data->refcount = NULL;
1278
1279 stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+");
1280
1281 return stream;
1282 }
1283 /* }}} */
1284
1285 /* {{{ php_ssh2_fopen_wrapper_tunnel
1286 * ssh2.tunnel:// fopen wrapper
1287 */
php_ssh2_fopen_wrapper_tunnel(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)1288 static php_stream *php_ssh2_fopen_wrapper_tunnel(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
1289 {
1290 LIBSSH2_SESSION *session = NULL;
1291 php_stream *stream = NULL;
1292 php_url *resource;
1293 char *host = NULL;
1294 int port = 0;
1295 zend_resource *rsrc;
1296
1297 resource = php_ssh2_fopen_wraper_parse_path(path, "tunnel", context, &session, &rsrc, NULL, NULL);
1298 if (!resource || !session) {
1299 return NULL;
1300 }
1301
1302 if (resource->path && SSH2_URL_STR(resource->path)[0] == '/') {
1303 char *colon;
1304
1305 host = SSH2_URL_STR(resource->path) + 1;
1306 if (*host == '[') {
1307 /* IPv6 Encapsulated Format */
1308 host++;
1309 colon = strstr(host, "]:");
1310 if (colon) {
1311 *colon = 0;
1312 colon += 2;
1313 }
1314 } else {
1315 colon = strchr(host, ':');
1316 if (colon) {
1317 *(colon++) = 0;
1318 }
1319 }
1320 if (colon) {
1321 port = atoi(colon);
1322 }
1323 }
1324
1325 if ((port <= 0) || (port > 65535) || !host || (strlen(host) == 0)) {
1326 /* Invalid connection criteria */
1327 php_url_free(resource);
1328 zend_list_delete(rsrc);
1329 return NULL;
1330 }
1331
1332 stream = php_ssh2_direct_tcpip(session, rsrc, host, port);
1333 if (!stream) {
1334 zend_list_delete(rsrc);
1335 }
1336 php_url_free(resource);
1337
1338 return stream;
1339 }
1340 /* }}} */
1341
1342 static php_stream_wrapper_ops php_ssh2_tunnel_stream_wops = {
1343 php_ssh2_fopen_wrapper_tunnel,
1344 NULL, /* stream_close */
1345 NULL, /* stat */
1346 NULL, /* stat_url */
1347 NULL, /* opendir */
1348 "ssh2.tunnel"
1349 };
1350
1351 php_stream_wrapper php_ssh2_stream_wrapper_tunnel = {
1352 &php_ssh2_tunnel_stream_wops,
1353 NULL,
1354 0
1355 };
1356
1357 /* {{{ proto stream ssh2_tunnel(resource session, string host, int port)
1358 * Tunnel to remote TCP/IP host/port
1359 */
PHP_FUNCTION(ssh2_tunnel)1360 PHP_FUNCTION(ssh2_tunnel)
1361 {
1362 LIBSSH2_SESSION *session;
1363 php_stream *stream;
1364 zval *zsession;
1365 char *host;
1366 size_t host_len;
1367 zend_long port;
1368
1369 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsl", &zsession, &host, &host_len, &port) == FAILURE) {
1370 return;
1371 }
1372
1373 SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
1374
1375 stream = php_ssh2_direct_tcpip(session, Z_RES_P(zsession), host, port);
1376 if (!stream) {
1377 RETURN_FALSE;
1378 }
1379
1380 /* Ensure that channels are freed BEFORE the sessions they belong to */
1381 Z_ADDREF_P(zsession);
1382
1383 php_stream_to_zval(stream, return_value);
1384 }
1385 /* }}} */
1386
1387 /* ******************
1388 * Generic Helper *
1389 ****************** */
1390
1391 /* {{{ proto stream ssh2_fetch_stream(stream channel, int streamid)
1392 * Fetch an extended data stream
1393 */
PHP_FUNCTION(ssh2_fetch_stream)1394 PHP_FUNCTION(ssh2_fetch_stream)
1395 {
1396 php_ssh2_channel_data *data, *stream_data;
1397 php_stream *parent, *stream;
1398 zval *zparent;
1399 zend_long streamid;
1400
1401 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &zparent, &streamid) == FAILURE) {
1402 return;
1403 }
1404
1405 if (streamid < 0) {
1406 php_error_docref(NULL, E_WARNING, "Invalid stream ID requested");
1407 RETURN_FALSE;
1408 }
1409
1410 php_stream_from_zval(parent, zparent);
1411
1412 if (parent->ops != &php_ssh2_channel_stream_ops) {
1413 php_error_docref(NULL, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME);
1414 RETURN_FALSE;
1415 }
1416
1417 data = (php_ssh2_channel_data*)parent->abstract;
1418
1419 if (!data->refcount) {
1420 data->refcount = emalloc(sizeof(unsigned char));
1421 *(data->refcount) = 1;
1422 }
1423
1424 if (*(data->refcount) == 255) {
1425 php_error_docref(NULL, E_WARNING, "Too many streams associated to a single channel");
1426 RETURN_FALSE;
1427 }
1428
1429 (*(data->refcount))++;
1430
1431 stream_data = emalloc(sizeof(php_ssh2_channel_data));
1432 memcpy(stream_data, data, sizeof(php_ssh2_channel_data));
1433 stream_data->streamid = streamid;
1434
1435 stream = php_stream_alloc(&php_ssh2_channel_stream_ops, stream_data, 0, "r+");
1436 if (!stream) {
1437 php_error_docref(NULL, E_WARNING, "Error opening substream");
1438 efree(stream_data);
1439 (data->refcount)--;
1440 RETURN_FALSE;
1441 }
1442
1443 php_stream_to_zval(stream, return_value);
1444 }
1445 /* }}} */
1446
1447 /* {{{ proto stream ssh2_send_eof(stream channel)
1448 * Sends EOF to a stream. Primary use is to close stdin of an stdio stream.
1449 */
PHP_FUNCTION(ssh2_send_eof)1450 PHP_FUNCTION(ssh2_send_eof)
1451 {
1452 php_ssh2_channel_data *data;
1453 php_stream *parent;
1454 zval *zparent;
1455 int ssh2_ret;
1456
1457 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zparent) == FAILURE) {
1458 return;
1459 }
1460
1461 php_stream_from_zval(parent, zparent);
1462 if (parent->ops != &php_ssh2_channel_stream_ops) {
1463 php_error_docref(NULL, E_WARNING, "Provided stream is not of type " PHP_SSH2_CHANNEL_STREAM_NAME);
1464 RETURN_FALSE;
1465 }
1466
1467 data = (php_ssh2_channel_data*)parent->abstract;
1468 if (!data) {
1469 php_error_docref(NULL, E_WARNING, "Abstract in stream is null");
1470 RETURN_FALSE;
1471 }
1472
1473 ssh2_ret = libssh2_channel_send_eof(data->channel);
1474 if (ssh2_ret < 0) {
1475 php_error_docref(NULL, E_WARNING, "Couldn't send EOF to channel (Return code %d)", ssh2_ret);
1476 RETURN_FALSE;
1477 }
1478
1479 RETURN_TRUE;
1480 }
1481 /* }}} */
1482
1483 /*
1484 * Local variables:
1485 * tab-width: 4
1486 * c-basic-offset: 4
1487 * End:
1488 * vim600: noet sw=4 ts=4 fdm=marker
1489 * vim<600: noet sw=4 ts=4
1490 */
1491