source: mainline/uspace/srv/hid/remcons/user.c@ 2a180307

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2a180307 was 2a180307, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

remcons: ignore telnet commands

  • Property mode set to 100644
File size: 7.7 KB
Line 
1/*
2 * Copyright (c) 2012 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup remcons
30 * @{
31 */
32/** @file
33 */
34#include <async.h>
35#include <stdio.h>
36#include <adt/prodcons.h>
37#include <ipc/input.h>
38#include <ipc/console.h>
39#include <ipc/vfs.h>
40#include <errno.h>
41#include <str_error.h>
42#include <loc.h>
43#include <event.h>
44#include <io/keycode.h>
45#include <align.h>
46#include <malloc.h>
47#include <as.h>
48#include <fibril_synch.h>
49#include <task.h>
50#include <net/in.h>
51#include <net/inet.h>
52#include <net/socket.h>
53#include <io/console.h>
54#include <inttypes.h>
55#include <assert.h>
56#include "user.h"
57#include "telnet.h"
58
59static FIBRIL_MUTEX_INITIALIZE(users_guard);
60static LIST_INITIALIZE(users);
61
62/** Create new telnet user.
63 *
64 * @param socket Socket the user communicates through.
65 * @return New telnet user or NULL when out of memory.
66 */
67telnet_user_t *telnet_user_create(int socket)
68{
69 static int telnet_user_id_counter = 0;
70
71 telnet_user_t *user = malloc(sizeof(telnet_user_t));
72 if (user == NULL) {
73 return NULL;
74 }
75
76 user->id = ++telnet_user_id_counter;
77
78 int rc = asprintf(&user->service_name, "%s/telnet%d", NAMESPACE, user->id);
79 if (rc < 0) {
80 free(user);
81 return NULL;
82 }
83
84 user->socket = socket;
85 user->service_id = (service_id_t) -1;
86 prodcons_initialize(&user->in_events);
87 link_initialize(&user->link);
88 user->socket_buffer_len = 0;
89 user->socket_buffer_pos = 0;
90
91 fibril_condvar_initialize(&user->refcount_cv);
92 fibril_mutex_initialize(&user->guard);
93 user->task_finished = false;
94 user->socket_closed = false;
95 user->locsrv_connection_count = 0;
96
97
98 fibril_mutex_lock(&users_guard);
99 list_append(&user->link, &users);
100 fibril_mutex_unlock(&users_guard);
101
102 return user;
103}
104
105/** Destroy telnet user structure.
106 *
107 * @param user User to be destroyed.
108 */
109void telnet_user_destroy(telnet_user_t *user)
110{
111 assert(user);
112
113 fibril_mutex_lock(&users_guard);
114 list_remove(&user->link);
115 fibril_mutex_unlock(&users_guard);
116
117 free(user);
118}
119
120/** Find user by service id and increments reference counter.
121 *
122 * @param id Location service id of the telnet user's terminal.
123 */
124telnet_user_t *telnet_user_get_for_client_connection(service_id_t id)
125{
126 telnet_user_t *user = NULL;
127
128 fibril_mutex_lock(&users_guard);
129 list_foreach(users, link) {
130 telnet_user_t *tmp = list_get_instance(link, telnet_user_t, link);
131 if (tmp->service_id == id) {
132 user = tmp;
133 break;
134 }
135 }
136 if (user == NULL) {
137 fibril_mutex_unlock(&users_guard);
138 return NULL;
139 }
140
141 telnet_user_t *tmp = user;
142 fibril_mutex_lock(&tmp->guard);
143 user->locsrv_connection_count++;
144
145 /*
146 * Refuse to return user whose task already finished or when
147 * the socket is already closed().
148 */
149 if (user->task_finished || user->socket_closed) {
150 user = NULL;
151 user->locsrv_connection_count--;
152 }
153
154 fibril_mutex_unlock(&tmp->guard);
155
156
157 fibril_mutex_unlock(&users_guard);
158
159 return user;
160}
161
162/** Notify that client disconnected from the remote terminal.
163 *
164 * @param user To which user the client was connected.
165 */
166void telnet_user_notify_client_disconnected(telnet_user_t *user)
167{
168 fibril_mutex_lock(&user->guard);
169 assert(user->locsrv_connection_count > 0);
170 user->locsrv_connection_count--;
171 fibril_condvar_signal(&user->refcount_cv);
172 fibril_mutex_unlock(&user->guard);
173}
174
175/** Tell whether the launched task already exited and socket is already closed.
176 *
177 * @param user Telnet user in question.
178 */
179bool telnet_user_is_zombie(telnet_user_t *user)
180{
181 fibril_mutex_lock(&user->guard);
182 bool zombie = user->socket_closed || user->task_finished;
183 fibril_mutex_unlock(&user->guard);
184
185 return zombie;
186}
187
188/** Receive next byte from a socket (use buffering.
189 * We need to return the value via extra argument because the read byte
190 * might be negative.
191 */
192static int telnet_user_recv_next_byte_no_lock(telnet_user_t *user, char *byte)
193{
194 /* No more buffered data? */
195 if (user->socket_buffer_len <= user->socket_buffer_pos) {
196 int recv_length = recv(user->socket, user->socket_buffer, BUFFER_SIZE, 0);
197 if ((recv_length == 0) || (recv_length == ENOTCONN)) {
198 user->socket_closed = true;
199 return ENOENT;
200 }
201 if (recv_length < 0) {
202 return recv_length;
203 }
204 user->socket_buffer_len = recv_length;
205 user->socket_buffer_pos = 0;
206 }
207
208 *byte = user->socket_buffer[user->socket_buffer_pos++];
209
210 return EOK;
211}
212
213/** Creates new keyboard event from given char.
214 *
215 * @param type Event type (press / release).
216 * @param c Pressed character.
217 */
218static kbd_event_t* new_kbd_event(kbd_event_type_t type, wchar_t c) {
219 kbd_event_t *event = malloc(sizeof(kbd_event_t));
220 assert(event);
221
222 link_initialize(&event->link);
223 event->type = type;
224 event->c = c;
225 event->mods = 0;
226 event->key = (c == '\n' ? KC_ENTER : KC_A);
227
228 return event;
229}
230
231static void process_telnet_command(telnet_user_t *user,
232 telnet_cmd_t option_code, telnet_cmd_t cmd)
233{
234 if (option_code != 0) {
235 telnet_user_log(user, "Ignoring telnet command %u %u %u.",
236 TELNET_IAC, option_code, cmd);
237 } else {
238 telnet_user_log(user, "Ignoring telnet command %u %u.",
239 TELNET_IAC, cmd);
240 }
241}
242
243int telnet_user_get_next_keyboard_event(telnet_user_t *user, kbd_event_t *event)
244{
245 fibril_mutex_lock(&user->guard);
246 if (list_empty(&user->in_events.list)) {
247 char next_byte = 0;
248 bool inside_telnet_command = false;
249
250 telnet_cmd_t telnet_option_code = 0;
251
252 /* Skip zeros, bail-out on error. */
253 while (next_byte == 0) {
254 int rc = telnet_user_recv_next_byte_no_lock(user, &next_byte);
255 if (rc != EOK) {
256 fibril_mutex_unlock(&user->guard);
257 return rc;
258 }
259 uint8_t byte = (uint8_t) next_byte;
260
261 /* Skip telnet commands. */
262 if (inside_telnet_command) {
263 inside_telnet_command = false;
264 next_byte = 0;
265 if (TELNET_IS_OPTION_CODE(byte)) {
266 telnet_option_code = byte;
267 inside_telnet_command = true;
268 } else {
269 process_telnet_command(user,
270 telnet_option_code, byte);
271 }
272 }
273 if (byte == TELNET_IAC) {
274 inside_telnet_command = true;
275 next_byte = 0;
276 }
277 }
278
279 /* CR-LF conversions. */
280 if (next_byte == 13) {
281 next_byte = 10;
282 }
283
284 kbd_event_t *down = new_kbd_event(KEY_PRESS, next_byte);
285 kbd_event_t *up = new_kbd_event(KEY_RELEASE, next_byte);
286 assert(down);
287 assert(up);
288 prodcons_produce(&user->in_events, &down->link);
289 prodcons_produce(&user->in_events, &up->link);
290 }
291
292 link_t *link = prodcons_consume(&user->in_events);
293 kbd_event_t *tmp = list_get_instance(link, kbd_event_t, link);
294
295 fibril_mutex_unlock(&user->guard);
296
297 *event = *tmp;
298
299 free(tmp);
300
301 return EOK;
302}
303
304/**
305 * @}
306 */
Note: See TracBrowser for help on using the repository browser.