blob: 218f9abf3499f6c5ded3d67e90ac8fa15a67cbc2 [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/* minimalistic telnet/network interface it might turn into a wire interface */
2/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21#include <sys/socket.h>
22#include <netinet/in.h>
Sylvain Munauta9efc122012-03-01 20:41:40 +010023#include <errno.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020024#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <unistd.h>
28
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010029#include <osmocom/core/msgb.h>
Sylvain Munauta9efc122012-03-01 20:41:40 +010030#include <osmocom/core/socket.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010031#include <osmocom/core/talloc.h>
32#include <osmocom/core/logging.h>
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +020033#include <osmocom/core/signal.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034
35#include <osmocom/vty/telnet_interface.h>
36#include <osmocom/vty/buffer.h>
Holger Hans Peter Freyther2e228fc2010-09-11 13:41:41 +080037#include <osmocom/vty/command.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038
Harald Weltee881b1b2011-08-17 18:52:30 +020039/*! \addtogroup telnet_interface
Harald Welte7acb30c2011-08-17 17:13:48 +020040 * @{
41 */
42/*! \file telnet_interface.c */
43
Harald Welte3fb0b6f2010-05-19 19:02:52 +020044/* per connection data */
45LLIST_HEAD(active_connections);
46
47static void *tall_telnet_ctx;
48
49/* per network data */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020050static int telnet_new_connection(struct osmo_fd *fd, unsigned int what);
Harald Welte3fb0b6f2010-05-19 19:02:52 +020051
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020052static struct osmo_fd server_socket = {
Harald Welte3fb0b6f2010-05-19 19:02:52 +020053 .when = BSC_FD_READ,
54 .cb = telnet_new_connection,
55 .priv_nr = 0,
56};
57
Sylvain Munauta9efc122012-03-01 20:41:40 +010058/*! \brief Initialize telnet based VTY interface listening to 127.0.0.1
Harald Welte7acb30c2011-08-17 17:13:48 +020059 * \param[in] tall_ctx \ref talloc context
60 * \param[in] priv private data to be passed to callback
61 * \param[in] port UDP port number
62 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020063int telnet_init(void *tall_ctx, void *priv, int port)
64{
Sylvain Munauta9efc122012-03-01 20:41:40 +010065 return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port);
66}
67
68/*! \brief Initialize telnet based VTY interface
69 * \param[in] tall_ctx \ref talloc context
70 * \param[in] priv private data to be passed to callback
71 * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...)
72 * \param[in] port UDP port number
73 */
74int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
75{
76 int rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020077
78 tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
Sylvain Munauta9efc122012-03-01 20:41:40 +010079 "telnet_connection");
Harald Welte3fb0b6f2010-05-19 19:02:52 +020080
Sylvain Munauta9efc122012-03-01 20:41:40 +010081 rc = osmo_sock_init_ofd(
82 &server_socket,
83 AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
84 ip, port, OSMO_SOCK_F_BIND
85 );
Harald Welte3fb0b6f2010-05-19 19:02:52 +020086
87 server_socket.data = priv;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020088
Neels Hofmeyr55dc2ed2016-09-19 14:10:25 +020089 if (rc < 0) {
90 LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n",
91 ip, port);
92 return -1;
93 }
94
95 LOGP(DLGLOBAL, LOGL_NOTICE, "telnet at %s %d\n", ip, port);
96 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020097}
98
Holger Hans Peter Freyther2e228fc2010-09-11 13:41:41 +080099extern struct host host;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200100
Harald Welte7acb30c2011-08-17 17:13:48 +0200101/*! \brief close a telnet connection */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200102int telnet_close_client(struct osmo_fd *fd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200103{
104 struct telnet_connection *conn = (struct telnet_connection*)fd->data;
105
106 close(fd->fd);
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200107 osmo_fd_unregister(fd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200108
109 if (conn->dbg) {
110 log_del_target(conn->dbg);
111 talloc_free(conn->dbg);
112 }
113
114 llist_del(&conn->entry);
115 talloc_free(conn);
116 return 0;
117}
118
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200119static int client_data(struct osmo_fd *fd, unsigned int what)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200120{
121 struct telnet_connection *conn = fd->data;
122 int rc = 0;
123
124 if (what & BSC_FD_READ) {
125 conn->fd.when &= ~BSC_FD_READ;
126 rc = vty_read(conn->vty);
127 }
128
129 /* vty might have been closed from vithin vty_read() */
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +0200130 if (rc == -EBADF)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200131 return rc;
132
133 if (what & BSC_FD_WRITE) {
134 rc = buffer_flush_all(conn->vty->obuf, fd->fd);
135 if (rc == BUFFER_EMPTY)
136 conn->fd.when &= ~BSC_FD_WRITE;
137 }
138
139 return rc;
140}
141
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200142static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200143{
144 struct telnet_connection *connection;
145 struct sockaddr_in sockaddr;
146 socklen_t len = sizeof(sockaddr);
147 int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
Harald Welte4a1cb092016-11-26 00:11:53 +0100148 int rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200149
150 if (new_connection < 0) {
151 LOGP(0, LOGL_ERROR, "telnet accept failed\n");
152 return new_connection;
153 }
154
155 connection = talloc_zero(tall_telnet_ctx, struct telnet_connection);
156 connection->priv = fd->data;
157 connection->fd.data = connection;
158 connection->fd.fd = new_connection;
159 connection->fd.when = BSC_FD_READ;
160 connection->fd.cb = client_data;
Harald Welte4a1cb092016-11-26 00:11:53 +0100161 rc = osmo_fd_register(&connection->fd);
162 if (rc < 0) {
163 talloc_free(connection);
164 return rc;
165 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200166 llist_add_tail(&connection->entry, &active_connections);
167
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200168 connection->vty = vty_create(new_connection, connection);
169 if (!connection->vty) {
170 LOGP(0, LOGL_ERROR, "couldn't create VTY\n");
171 close(new_connection);
172 talloc_free(connection);
173 return -1;
174 }
175
176 return 0;
177}
178
Harald Welte7acb30c2011-08-17 17:13:48 +0200179/*! \brief callback from core VTY code about VTY related events */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200180void vty_event(enum event event, int sock, struct vty *vty)
181{
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +0200182 struct vty_signal_data sig_data = { 0, };
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200183 struct telnet_connection *connection = vty->priv;
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +0200184 struct osmo_fd *bfd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200185
186 if (vty->type != VTY_TERM)
187 return;
188
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +0200189 sig_data.event = event;
190 sig_data.sock = sock;
191 sig_data.vty = vty;
192 osmo_signal_dispatch(SS_L_VTY, S_VTY_EVENT, &sig_data);
193
194 if (!connection)
195 return;
196
197 bfd = &connection->fd;
198
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200199 switch (event) {
200 case VTY_READ:
201 bfd->when |= BSC_FD_READ;
202 break;
203 case VTY_WRITE:
204 bfd->when |= BSC_FD_WRITE;
205 break;
206 case VTY_CLOSED:
207 /* vty layer is about to free() vty */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200208 telnet_close_client(bfd);
209 break;
210 default:
211 break;
212 }
213}
214
Andreas.Eversbergdc3be0a2011-11-06 20:09:28 +0100215void telnet_exit(void)
216{
217 struct telnet_connection *tc, *tc2;
218
219 llist_for_each_entry_safe(tc, tc2, &active_connections, entry)
220 telnet_close_client(&tc->fd);
221
222 osmo_fd_unregister(&server_socket);
223 close(server_socket.fd);
224 talloc_free(tall_telnet_ctx);
225}
226
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200227/*! @} */