telnet.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:20k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * telnet.c: VLM interface plugin
  3.  *****************************************************************************
  4.  * Copyright (C) 2000-2006 the VideoLAN team
  5.  * $Id: 9fbc52461d8011d29616dccd520d39b88f7b4e97 $
  6.  *
  7.  * Authors: Simon Latapie <garf@videolan.org>
  8.  *          Laurent Aimar <fenrir@videolan.org>
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23.  *****************************************************************************/
  24. /*****************************************************************************
  25.  * Preamble
  26.  *****************************************************************************/
  27. #ifdef HAVE_CONFIG_H
  28. # include "config.h"
  29. #endif
  30. #include <vlc_common.h>
  31. #include <vlc_plugin.h>
  32. #include <vlc_interface.h>
  33. #include <vlc_input.h>
  34. #include <stdbool.h>
  35. #include <sys/stat.h>
  36. #include <errno.h>
  37. #include <fcntl.h>
  38. #ifdef HAVE_SYS_TIME_H
  39. #    include <sys/time.h>
  40. #endif
  41. #ifdef HAVE_UNISTD_H
  42. #   include <unistd.h>
  43. #endif
  44. #ifdef HAVE_POLL
  45. #   include <poll.h>
  46. #endif
  47. #include <vlc_network.h>
  48. #include <vlc_url.h>
  49. #include <vlc_vlm.h>
  50. #define READ_MODE_PWD 1
  51. #define READ_MODE_CMD 2
  52. #define WRITE_MODE_PWD 3 // when we write the word "Password:"
  53. #define WRITE_MODE_CMD 4
  54. /* telnet commands */
  55. #define TEL_WILL    251
  56. #define TEL_WONT    252
  57. #define TEL_DO      253
  58. #define TEL_DONT    254
  59. #define TEL_IAC     255
  60. #define TEL_ECHO    1
  61. /*****************************************************************************
  62.  * Module descriptor
  63.  *****************************************************************************/
  64. static int  Open ( vlc_object_t * );
  65. static void Close( vlc_object_t * );
  66. #define TELNETHOST_TEXT N_( "Host" )
  67. #define TELNETHOST_LONGTEXT N_( "This is the host on which the " 
  68.     "interface will listen. It defaults to all network interfaces (0.0.0.0)." 
  69.     " If you want this interface to be available only on the local " 
  70.     "machine, enter "127.0.0.1"." )
  71. #define TELNETPORT_TEXT N_( "Port" )
  72. #define TELNETPORT_LONGTEXT N_( "This is the TCP port on which this " 
  73.     "interface will listen. It defaults to 4212." )
  74. #define TELNETPORT_DEFAULT 4212
  75. #define TELNETPWD_TEXT N_( "Password" )
  76. #define TELNETPWD_LONGTEXT N_( "A single administration password is used " 
  77.     "to protect this interface. The default value is "admin"." )
  78. #define TELNETPWD_DEFAULT "admin"
  79. vlc_module_begin ()
  80.     set_shortname( "Telnet" )
  81.     set_category( CAT_INTERFACE )
  82.     set_subcategory( SUBCAT_INTERFACE_CONTROL )
  83.     add_string( "telnet-host", "", NULL, TELNETHOST_TEXT,
  84.                  TELNETHOST_LONGTEXT, true )
  85.     add_integer( "telnet-port", TELNETPORT_DEFAULT, NULL, TELNETPORT_TEXT,
  86.                  TELNETPORT_LONGTEXT, true )
  87.     add_password( "telnet-password", TELNETPWD_DEFAULT, NULL, TELNETPWD_TEXT,
  88.                 TELNETPWD_LONGTEXT, true )
  89.     set_description( N_("VLM remote control interface") )
  90.     add_category_hint( "VLM", NULL, false )
  91.     set_capability( "interface", 0 )
  92.     set_callbacks( Open , Close )
  93. vlc_module_end ()
  94. /*****************************************************************************
  95.  * Local prototypes.
  96.  *****************************************************************************/
  97. static void Run( intf_thread_t * );
  98. typedef struct
  99. {
  100.     int        i_mode; /* read or write */
  101.     int        fd;
  102.     char       buffer_read[1000]; // 1000 byte per command should be sufficient
  103.     char      *buffer_write;
  104.     char      *p_buffer_read;
  105.     char      *p_buffer_write; // the position in the buffer
  106.     int        i_buffer_write; // the number of byte we still have to send
  107.     int        i_tel_cmd; // for specific telnet commands
  108. } telnet_client_t;
  109. static char *MessageToString( vlm_message_t *, int );
  110. static void Write_message( telnet_client_t *, vlm_message_t *, const char *, int );
  111. struct intf_sys_t
  112. {
  113.    telnet_client_t **clients;
  114.    int             i_clients;
  115.    int             *pi_fd;
  116.    vlm_t           *mediatheque;
  117. };
  118. /*
  119.  * getPort: Decide which port to use. There are two possibilities to
  120.  * specify a port: integrated in the --telnet-host option with :PORT
  121.  * or using the --telnet-port option. The --telnet-port option has
  122.  * precedence.
  123.  * This code relies upon the fact the url.i_port is 0 if the :PORT
  124.  * option is missing from --telnet-host.
  125.  */
  126. static int getPort(intf_thread_t *p_intf, const vlc_url_t *url, int i_port)
  127. {
  128.     if (i_port == TELNETPORT_DEFAULT && url->i_port != 0)
  129.         i_port = url->i_port;
  130.     if (url->i_port != 0 && url->i_port != i_port)
  131.         // Print error if two different ports have been specified
  132.         msg_Warn( p_intf, "ignoring port %d (using %d)", url->i_port, i_port );
  133.     return i_port;
  134. }
  135. /*****************************************************************************
  136.  * Open: initialize dummy interface
  137.  *****************************************************************************/
  138. static int Open( vlc_object_t *p_this )
  139. {
  140.     intf_thread_t *p_intf = (intf_thread_t*) p_this;
  141.     vlm_t *mediatheque;
  142.     char *psz_address;
  143.     vlc_url_t url;
  144.     int i_telnetport;
  145.     if( !(mediatheque = vlm_New( p_intf )) )
  146.     {
  147.         msg_Err( p_intf, "cannot start VLM" );
  148.         return VLC_EGENERIC;
  149.     }
  150.     msg_Info( p_intf, "using the VLM interface plugin..." );
  151.     i_telnetport = config_GetInt( p_intf, "telnet-port" );
  152.     psz_address  = config_GetPsz( p_intf, "telnet-host" );
  153.     vlc_UrlParse(&url, psz_address, 0);
  154.     free( psz_address );
  155.     // There might be two ports given, resolve any potentially
  156.     // conflict
  157.     url.i_port = getPort(p_intf, &url, i_telnetport);
  158.     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
  159.     if( !p_intf->p_sys )
  160.     {
  161.         vlm_Delete( mediatheque );
  162.         vlc_UrlClean( &url );
  163.         return VLC_ENOMEM;
  164.     }
  165.     if( ( p_intf->p_sys->pi_fd = net_ListenTCP( p_intf, url.psz_host, url.i_port ) ) == NULL )
  166.     {
  167.         msg_Err( p_intf, "cannot listen for telnet" );
  168.         vlm_Delete( mediatheque );
  169.         vlc_UrlClean( &url );
  170.         free( p_intf->p_sys );
  171.         return VLC_EGENERIC;
  172.     }
  173.     msg_Info( p_intf,
  174.               "telnet interface started on interface %s %d",
  175.               url.psz_host, url.i_port );
  176.     p_intf->p_sys->i_clients   = 0;
  177.     p_intf->p_sys->clients     = NULL;
  178.     p_intf->p_sys->mediatheque = mediatheque;
  179.     p_intf->pf_run = Run;
  180.     vlc_UrlClean( &url );
  181.     return VLC_SUCCESS;
  182. }
  183. /*****************************************************************************
  184.  * Close:
  185.  *****************************************************************************/
  186. static void Close( vlc_object_t *p_this )
  187. {
  188.     intf_thread_t *p_intf = (intf_thread_t*)p_this;
  189.     intf_sys_t    *p_sys  = p_intf->p_sys;
  190.     int i;
  191.     for( i = 0; i < p_sys->i_clients; i++ )
  192.     {
  193.         telnet_client_t *cl = p_sys->clients[i];
  194.         net_Close( cl->fd );
  195.         free( cl->buffer_write );
  196.         free( cl );
  197.     }
  198.     free( p_sys->clients );
  199.     net_ListenClose( p_sys->pi_fd );
  200.     vlm_Delete( p_sys->mediatheque );
  201.     free( p_sys );
  202. }
  203. /*****************************************************************************
  204.  * Run: main loop
  205.  *****************************************************************************/
  206. static void Run( intf_thread_t *p_intf )
  207. {
  208.     intf_sys_t     *p_sys = p_intf->p_sys;
  209.     char           *psz_password;
  210.     unsigned        nlisten = 0;
  211.     for (const int *pfd = p_sys->pi_fd; *pfd != -1; pfd++)
  212.         nlisten++; /* How many listening sockets do we have? */
  213.     /* FIXME: make sure config_* is cancel-safe */
  214.     psz_password = config_GetPsz( p_intf, "telnet-password" );
  215.     vlc_cleanup_push( free, psz_password );
  216.     for( ;; )
  217.     {
  218.         unsigned ncli = p_sys->i_clients;
  219.         struct pollfd ufd[ncli + nlisten];
  220.         for (unsigned i = 0; i < ncli; i++)
  221.         {
  222.             telnet_client_t *cl = p_sys->clients[i];
  223.             ufd[i].fd = cl->fd;
  224.             if( (cl->i_mode == WRITE_MODE_PWD) || (cl->i_mode == WRITE_MODE_CMD) )
  225.                 ufd[i].events = POLLOUT;
  226.             else
  227.                 ufd[i].events = POLLIN;
  228.             ufd[i].revents = 0;
  229.         }
  230.         for (unsigned i = 0; i < nlisten; i++)
  231.         {
  232.             ufd[ncli + i].fd = p_sys->pi_fd[i];
  233.             ufd[ncli + i].events = POLLIN;
  234.             ufd[ncli + i].revents = 0;
  235.         }
  236.         switch (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), -1))
  237.         {
  238.             case -1:
  239.                 if (net_errno != EINTR)
  240.                 {
  241.                     msg_Err (p_intf, "network poll error");
  242.                     msleep (1000);
  243.                     continue;
  244.                 }
  245.             case 0:
  246.                 continue;
  247.         }
  248.         int canc = vlc_savecancel ();
  249.         /* check if there is something to do with the socket */
  250.         for (unsigned i = 0; i < ncli; i++)
  251.         {
  252.             telnet_client_t *cl = p_sys->clients[i];
  253.             if (ufd[i].revents & (POLLERR|POLLHUP))
  254.             {
  255.             drop:
  256.                 net_Close( cl->fd );
  257.                 TAB_REMOVE( p_intf->p_sys->i_clients ,
  258.                             p_intf->p_sys->clients , cl );
  259.                 free( cl->buffer_write );
  260.                 free( cl );
  261.                 continue;
  262.             }
  263.             if (ufd[i].revents & POLLOUT && (cl->i_buffer_write > 0))
  264.             {
  265.                 ssize_t i_len;
  266.                 i_len = send( cl->fd, cl->p_buffer_write ,
  267.                               cl->i_buffer_write, 0 );
  268.                 if( i_len > 0 )
  269.                 {
  270.                     cl->p_buffer_write += i_len;
  271.                     cl->i_buffer_write -= i_len;
  272.                 }
  273.             }
  274.             if (ufd[i].revents & POLLIN)
  275.             {
  276.                 bool end = false;
  277.                 ssize_t i_recv;
  278.                 while( ((i_recv=recv( cl->fd, cl->p_buffer_read, 1, 0 )) > 0) &&
  279.                        ((cl->p_buffer_read - cl->buffer_read) < 999) )
  280.                 {
  281.                     switch( cl->i_tel_cmd )
  282.                     {
  283.                     case 0:
  284.                         switch( *(uint8_t *)cl->p_buffer_read )
  285.                         {
  286.                         case 'r':
  287.                             break;
  288.                         case 'n':
  289.                             *cl->p_buffer_read = 'n';
  290.                             end = true;
  291.                             break;
  292.                         case TEL_IAC: // telnet specific command
  293.                             cl->i_tel_cmd = 1;
  294.                             cl->p_buffer_read++;
  295.                             break;
  296.                         default:
  297.                             cl->p_buffer_read++;
  298.                             break;
  299.                         }
  300.                         break;
  301.                     case 1:
  302.                         switch( *(uint8_t *)cl->p_buffer_read )
  303.                         {
  304.                         case TEL_WILL: case TEL_WONT:
  305.                         case TEL_DO: case TEL_DONT:
  306.                             cl->i_tel_cmd++;
  307.                             cl->p_buffer_read++;
  308.                             break;
  309.                         default:
  310.                             cl->i_tel_cmd = 0;
  311.                             cl->p_buffer_read--;
  312.                             break;
  313.                         }
  314.                         break;
  315.                     case 2:
  316.                         cl->i_tel_cmd = 0;
  317.                         cl->p_buffer_read -= 2;
  318.                         break;
  319.                     }
  320.                     if( end ) break;
  321.                 }
  322.                 if( (cl->p_buffer_read - cl->buffer_read) == 999 )
  323.                 {
  324.                     Write_message( cl, NULL, "Line too longrn",
  325.                                    cl->i_mode + 2 );
  326.                 }
  327. #ifdef WIN32
  328.                 if( i_recv <= 0 && WSAGetLastError() == WSAEWOULDBLOCK )
  329.                 {
  330.                     errno = EAGAIN;
  331.                 }
  332. #endif
  333.                 if( i_recv == 0 || ( i_recv == -1 && ( end || errno != EAGAIN ) ) )
  334.                     goto drop;
  335.             }
  336.         }
  337.         /* and now we should bidouille the data we received / send */
  338.         for(int i = 0 ; i < p_sys->i_clients ; i++ )
  339.         {
  340.             telnet_client_t *cl = p_sys->clients[i];
  341.             if( cl->i_mode >= WRITE_MODE_PWD && cl->i_buffer_write == 0 )
  342.             {
  343.                // we have finished to send
  344.                cl->i_mode -= 2; // corresponding READ MODE
  345.             }
  346.             else if( cl->i_mode == READ_MODE_PWD &&
  347.                      *cl->p_buffer_read == 'n' )
  348.             {
  349.                 *cl->p_buffer_read = '';
  350.                 if( !psz_password || !strcmp( psz_password, cl->buffer_read ) )
  351.                 {
  352.                     Write_message( cl, NULL, "xffxfcx01rnWelcome, "
  353.                                    "Masterrn> ", WRITE_MODE_CMD );
  354.                 }
  355.                 else
  356.                 {
  357.                     /* wrong password */
  358.                     Write_message( cl, NULL,
  359.                                    "rnWrong password.rnPassword: ",
  360.                                    WRITE_MODE_PWD );
  361.                 }
  362.             }
  363.             else if( cl->i_mode == READ_MODE_CMD &&
  364.                      *cl->p_buffer_read == 'n' )
  365.             {
  366.                 /* ok, here is a command line */
  367.                 if( !strncmp( cl->buffer_read, "logout", 6 ) ||
  368.                     !strncmp( cl->buffer_read, "quit", 4 )  ||
  369.                     !strncmp( cl->buffer_read, "exit", 4 ) )
  370.                 {
  371.                     net_Close( cl->fd );
  372.                     TAB_REMOVE( p_intf->p_sys->i_clients ,
  373.                                 p_intf->p_sys->clients , cl );
  374.                     free( cl->buffer_write );
  375.                     free( cl );
  376.                 }
  377.                 else if( !strncmp( cl->buffer_read, "shutdown", 8 ) )
  378.                 {
  379.                     msg_Err( p_intf, "shutdown requested" );
  380.                     libvlc_Quit( p_intf->p_libvlc );
  381.                 }
  382.                 else if( *cl->buffer_read == '@'
  383.                           && strchr( cl->buffer_read, ' ' ) )
  384.                 {
  385.                     /* Module specific commands (use same syntax as in the
  386.                      * rc interface) */
  387.                     char *psz_name = cl->buffer_read + 1;
  388.                     char *psz_cmd, *psz_arg, *psz_msg;
  389.                     int i_ret;
  390.                     psz_cmd = strchr( cl->buffer_read, ' ' );
  391.                     *psz_cmd = '';  psz_cmd++;
  392.                     if( ( psz_arg = strchr( psz_cmd, 'n' ) ) ) *psz_arg = '';
  393.                     if( ( psz_arg = strchr( psz_cmd, 'r' ) ) ) *psz_arg = '';
  394.                     if( ( psz_arg = strchr( psz_cmd, ' ' ) )
  395.                         && *psz_arg )
  396.                     {
  397.                         *psz_arg = '';
  398.                         psz_arg++;
  399.                     }
  400.                     i_ret = var_Command( p_intf, psz_name, psz_cmd, psz_arg,
  401.                                          &psz_msg );
  402.                     if( psz_msg )
  403.                     {
  404.                         vlm_message_t *message;
  405.                         message = vlm_MessageNew( "Module command", "%s", psz_msg );
  406.                         Write_message( cl, message, NULL, WRITE_MODE_CMD );
  407.                         vlm_MessageDelete( message );
  408.                         free( psz_msg );
  409.                     }
  410.                 }
  411.                 else
  412.                 {
  413.                     vlm_message_t *message;
  414.                     /* create a standard string */
  415.                     *cl->p_buffer_read = '';
  416.                     vlm_ExecuteCommand( p_sys->mediatheque, cl->buffer_read,
  417.                                         &message );
  418.                     if( !strncmp( cl->buffer_read, "help", 4 ) )
  419.                     {
  420.                         vlm_message_t *p_my_help =
  421.                             vlm_MessageSimpleNew( "Telnet Specific Commands:" );
  422.                         vlm_MessageAdd( p_my_help,
  423.                             vlm_MessageSimpleNew( "logout, quit, exit" ) );
  424.                         vlm_MessageAdd( p_my_help,
  425.                             vlm_MessageSimpleNew( "shutdown" ) );
  426.                         vlm_MessageAdd( p_my_help,
  427.                             vlm_MessageSimpleNew( "@moduleinstance command argument" ) );
  428.                         vlm_MessageAdd( message, p_my_help );
  429.                     }
  430.                     Write_message( cl, message, NULL, WRITE_MODE_CMD );
  431.                     vlm_MessageDelete( message );
  432.                 }
  433.             }
  434.         }
  435.         /* handle new connections */
  436.         for (unsigned i = 0; i < nlisten; i++)
  437.         {
  438.             int fd;
  439.             if (ufd[ncli + i].revents == 0)
  440.                 continue;
  441.             fd = net_AcceptSingle (VLC_OBJECT(p_intf), ufd[ncli + i].fd);
  442.             if (fd == -1)
  443.                 continue;
  444.             telnet_client_t *cl = calloc( 1, sizeof( telnet_client_t ));
  445.             if (cl == NULL)
  446.             {
  447.                 net_Close (fd);
  448.                 continue;
  449.             }
  450.             cl->i_tel_cmd = 0;
  451.             cl->fd = fd;
  452.             cl->buffer_write = NULL;
  453.             cl->p_buffer_write = cl->buffer_write;
  454.             Write_message( cl, NULL,
  455.                            "Password: xffxfbx01" , WRITE_MODE_PWD );
  456.             TAB_APPEND( p_sys->i_clients, p_sys->clients, cl );
  457.         }
  458.         vlc_restorecancel( canc );
  459.     }
  460.     vlc_cleanup_run ();
  461. }
  462. static void Write_message( telnet_client_t *client, vlm_message_t *message,
  463.                            const char *string_message, int i_mode )
  464. {
  465.     char *psz_message;
  466.     client->p_buffer_read = client->buffer_read;
  467.     (client->p_buffer_read)[0] = 0; // if (cl->p_buffer_read)[0] = 'n'
  468.     free( client->buffer_write );
  469.     /* generate the psz_message string */
  470.     if( message )
  471.     {
  472.         /* ok, look for vlm_message_t */
  473.         psz_message = MessageToString( message, 0 );
  474.     }
  475.     else
  476.     {
  477.         /* it is a basic string_message */
  478.         psz_message = strdup( string_message );
  479.     }
  480.     client->buffer_write = client->p_buffer_write = psz_message;
  481.     client->i_buffer_write = strlen( psz_message );
  482.     client->i_mode = i_mode;
  483. }
  484. /* We need the level of the message to put a beautiful indentation.
  485.  * first level is 0 */
  486. static char *MessageToString( vlm_message_t *message, int i_level )
  487. {
  488. #define STRING_CR "rn"
  489. #define STRING_TAIL "> "
  490.     char *psz_message;
  491.     int i, i_message = sizeof( STRING_TAIL );
  492.     if( !message || !message->psz_name )
  493.     {
  494.         return strdup( STRING_CR STRING_TAIL );
  495.     }
  496.     else if( !i_level && !message->i_child && !message->psz_value  )
  497.     {
  498.         /* A command is successful. Don't write anything */
  499.         return strdup( /*STRING_CR*/ STRING_TAIL );
  500.     }
  501.     i_message += strlen( message->psz_name ) + i_level * sizeof( "    " ) + 1;
  502.     psz_message = malloc( i_message );
  503.     *psz_message = 0;
  504.     for( i = 0; i < i_level; i++ ) strcat( psz_message, "    " );
  505.     strcat( psz_message, message->psz_name );
  506.     if( message->psz_value )
  507.     {
  508.         i_message += sizeof( " : " ) + strlen( message->psz_value ) +
  509.             sizeof( STRING_CR );
  510.         psz_message = realloc( psz_message, i_message );
  511.         strcat( psz_message, " : " );
  512.         strcat( psz_message, message->psz_value );
  513.         strcat( psz_message, STRING_CR );
  514.     }
  515.     else
  516.     {
  517.         i_message += sizeof( STRING_CR );
  518.         psz_message = realloc( psz_message, i_message );
  519.         strcat( psz_message, STRING_CR );
  520.     }
  521.     for( i = 0; i < message->i_child; i++ )
  522.     {
  523.         char *child_message =
  524.             MessageToString( message->child[i], i_level + 1 );
  525.         i_message += strlen( child_message );
  526.         psz_message = realloc( psz_message, i_message );
  527.         strcat( psz_message, child_message );
  528.         free( child_message );
  529.     }
  530.     if( i_level == 0 ) strcat( psz_message, STRING_TAIL );
  531.     return psz_message;
  532. }