/[LeafOK_CVS]/lbbs/src/bbs_net.c
ViewVC logotype

Contents of /lbbs/src/bbs_net.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.106 - (show annotations)
Sat Dec 20 07:18:36 2025 UTC (2 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.105: +2 -2 lines
Content type: text/x-csrc
Refine

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * bbs_net
4 * - user interactive feature of site shuttle
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "bbs.h"
14 #include "bbs_net.h"
15 #include "common.h"
16 #include "io.h"
17 #include "log.h"
18 #include "login.h"
19 #include "menu.h"
20 #include "screen.h"
21 #include "str_process.h"
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <netdb.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <arpa/inet.h>
32 #include <ctype.h>
33 #include <libssh/libssh.h>
34 #include <libssh/server.h>
35 #include <libssh/callbacks.h>
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38 #include <sys/select.h>
39 #include <sys/ioctl.h>
40 #include <sys/socket.h>
41
42 #ifdef HAVE_SYS_EPOLL_H
43 #include <sys/epoll.h>
44 #else
45 #include <poll.h>
46 #endif
47
48 static const char MENU_CONF_DELIM[] = " \t\r\n";
49
50 enum _bbs_net_constant_t
51 {
52 MAXSTATION = 26 * 2,
53 STATION_PER_LINE = 4,
54 USERNAME_MAX_LEN = 20,
55 PASSWORD_MAX_LEN = 20,
56 REMOTE_CONNECT_TIMEOUT = 10, // seconds
57 SSH_CONNECT_TIMEOUT = 5, // seconds
58 PROGRESS_BAR_LEN = 30,
59 };
60
61 struct _bbsnet_conf
62 {
63 char org_name[40];
64 char site_name[40];
65 char host_name[IP_ADDR_LEN];
66 char port[6];
67 int8_t use_ssh;
68 char charset[CHARSET_MAX_LEN + 1];
69 } bbsnet_conf[MAXSTATION];
70
71 static MENU_SET bbsnet_menu;
72
73 static void unload_bbsnet_conf(void);
74
75 static int load_bbsnet_conf(const char *file_config)
76 {
77 FILE *fp;
78 MENU *p_menu;
79 MENU_ITEM *p_menu_item;
80 MENU_ITEM_ID menu_item_id;
81 char line[LINE_BUFFER_LEN], *t1, *t2, *t3, *t4, *t5, *t6, *saveptr;
82 long port;
83 char *endptr;
84
85 unload_bbsnet_conf();
86
87 bbsnet_menu.p_menu_pool = calloc(1, sizeof(MENU));
88 if (bbsnet_menu.p_menu_pool == NULL)
89 {
90 log_error("calloc(p_menu_pool) error");
91 return -1;
92 }
93 bbsnet_menu.menu_count = 1;
94
95 bbsnet_menu.p_menu_item_pool = calloc(MAXSTATION, sizeof(MENU_ITEM));
96 if (bbsnet_menu.p_menu_item_pool == NULL)
97 {
98 log_error("calloc(p_menu_item_pool) error");
99 unload_bbsnet_conf();
100 return -1;
101 }
102 bbsnet_menu.menu_item_count = MAXSTATION;
103
104 p_menu = (MENU *)get_menu_by_id(&bbsnet_menu, 0);
105
106 strncpy(p_menu->name, "BBSNET", sizeof(p_menu->name) - 1);
107 p_menu->name[sizeof(p_menu->name) - 1] = '\0';
108 p_menu->title.show = 0;
109 p_menu->screen_show = 0;
110
111 fp = fopen(file_config, "r");
112 if (fp == NULL)
113 {
114 unload_bbsnet_conf();
115 return -2;
116 }
117
118 menu_item_id = 0;
119 while (fgets(line, sizeof(line), fp) && menu_item_id < MAXSTATION)
120 {
121 t1 = strtok_r(line, MENU_CONF_DELIM, &saveptr);
122 t2 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
123 t3 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
124 t4 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
125 t5 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
126 t6 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
127
128 if (t1 == NULL || t2 == NULL || t3 == NULL || t4 == NULL ||
129 t5 == NULL || t6 == NULL || t1[0] == '#')
130 {
131 continue;
132 }
133
134 strncpy(bbsnet_conf[menu_item_id].site_name, t2, sizeof(bbsnet_conf[menu_item_id].site_name) - 1);
135 bbsnet_conf[menu_item_id].site_name[sizeof(bbsnet_conf[menu_item_id].site_name) - 1] = '\0';
136 strncpy(bbsnet_conf[menu_item_id].org_name, t1, sizeof(bbsnet_conf[menu_item_id].org_name) - 1);
137 bbsnet_conf[menu_item_id].org_name[sizeof(bbsnet_conf[menu_item_id].org_name) - 1] = '\0';
138 strncpy(bbsnet_conf[menu_item_id].host_name, t3, sizeof(bbsnet_conf[menu_item_id].host_name) - 1);
139 bbsnet_conf[menu_item_id].host_name[sizeof(bbsnet_conf[menu_item_id].host_name) - 1] = '\0';
140 port = strtol(t4, &endptr, 10);
141 if (*endptr != '\0' || port <= 0 || port > 65535)
142 {
143 log_error("Invalid port value %ld of menu item %d", port, menu_item_id);
144 fclose(fp);
145 unload_bbsnet_conf();
146 return -3;
147 }
148 strncpy(bbsnet_conf[menu_item_id].port, t4, sizeof(bbsnet_conf[menu_item_id].port) - 1);
149 bbsnet_conf[menu_item_id].port[sizeof(bbsnet_conf[menu_item_id].port) - 1] = '\0';
150 bbsnet_conf[menu_item_id].use_ssh = (toupper(t5[0]) == 'Y');
151 strncpy(bbsnet_conf[menu_item_id].charset, t6, sizeof(bbsnet_conf[menu_item_id].charset) - 1);
152 bbsnet_conf[menu_item_id].charset[sizeof(bbsnet_conf[menu_item_id].charset) - 1] = '\0';
153
154 p_menu_item = get_menu_item_by_id(&bbsnet_menu, menu_item_id);
155 if (p_menu_item == NULL)
156 {
157 log_error("get_menu_item_by_id(%d) return NULL pointer", menu_item_id);
158 fclose(fp);
159 unload_bbsnet_conf();
160 return -3;
161 }
162
163 p_menu_item->row = (int16_t)(2 + menu_item_id / STATION_PER_LINE);
164 p_menu_item->col = (int16_t)(5 + menu_item_id % STATION_PER_LINE * 20);
165 snprintf(p_menu_item->action, sizeof(p_menu_item->action), "%d", (int16_t)menu_item_id);
166 p_menu_item->submenu = 0;
167 p_menu_item->priv = 0;
168 p_menu_item->level = 0;
169 p_menu_item->name[0] =
170 (char)(menu_item_id < MAXSTATION / 2 ? 'A' + menu_item_id : 'a' + menu_item_id - MAXSTATION / 2);
171 p_menu_item->name[1] = '\0';
172 snprintf(p_menu_item->text, sizeof(p_menu_item->text), "\033[1;36m%c.\033[m %s",
173 p_menu_item->name[0], bbsnet_conf[menu_item_id].site_name);
174
175 p_menu->items[p_menu->item_count] = menu_item_id;
176 p_menu->item_count++;
177 menu_item_id++;
178 }
179
180 bbsnet_menu.menu_item_count = (int16_t)menu_item_id;
181 bbsnet_menu.menu_id_path[0] = 0;
182 bbsnet_menu.menu_item_pos[0] = 0;
183 bbsnet_menu.choose_step = 0;
184
185 fclose(fp);
186
187 return 0;
188 }
189
190 static void unload_bbsnet_conf(void)
191 {
192 bbsnet_menu.menu_count = 0;
193 bbsnet_menu.menu_item_count = 0;
194
195 if (bbsnet_menu.p_menu_pool)
196 {
197 free(bbsnet_menu.p_menu_pool);
198 bbsnet_menu.p_menu_pool = NULL;
199 }
200
201 if (bbsnet_menu.p_menu_item_pool)
202 {
203 free(bbsnet_menu.p_menu_item_pool);
204 bbsnet_menu.p_menu_item_pool = NULL;
205 }
206 }
207
208 static void progress_bar(int percent, int len)
209 {
210 char line[LINE_BUFFER_LEN];
211 char buf[LINE_BUFFER_LEN];
212 char buf2[LINE_BUFFER_LEN];
213 int pos;
214
215 if (len < 4)
216 {
217 len = 4;
218 }
219 else if (len + 2 > LINE_BUFFER_LEN)
220 {
221 len = LINE_BUFFER_LEN - 3;
222 }
223 if (percent < 0)
224 {
225 percent = 0;
226 }
227 else if (percent > 100)
228 {
229 percent = 100;
230 }
231
232 pos = len * percent / 100;
233
234 line[0] = ' ';
235 for (int i = 1; i <= len; i++)
236 {
237 line[i] = '-';
238 }
239 line[len + 1] = ' ';
240 line[len + 2] = '\0';
241
242 snprintf(buf, sizeof(buf), "%*s%3d%%%*s",
243 (len - 4) / 2, "", percent, (len - 4 + 1) / 2, "");
244 memcpy(buf2, buf, (size_t)pos);
245 buf2[pos] = '\0';
246
247 moveto(4, 1);
248 prints("%s\r\n", line);
249 prints("|\033[46m%s\033[44m%s\033[m|\r\n", buf2, buf + pos);
250 prints("%s\r\n", line);
251 iflush();
252 }
253
254 static int progress_update(struct timespec *p_ts_begin, struct timespec *p_ts_now,
255 int total_time_ms, int *p_progress_last, int bar_len)
256 {
257 int progress;
258
259 if (clock_gettime(CLOCK_REALTIME, p_ts_now) == -1)
260 {
261 log_error("clock_gettime() error (%d)", errno);
262 return -1;
263 }
264
265 progress = (int)((p_ts_now->tv_sec - p_ts_begin->tv_sec) * 1000 +
266 (p_ts_now->tv_nsec - p_ts_begin->tv_nsec) / 1000 / 1000) /
267 REMOTE_CONNECT_TIMEOUT / 10 +
268 1;
269 if (progress < 0)
270 {
271 progress = 0;
272 }
273 if (progress > 100)
274 {
275 progress = 100;
276 }
277
278 if (progress != *p_progress_last)
279 {
280 *p_progress_last = progress;
281 progress_bar(progress, PROGRESS_BAR_LEN);
282 }
283
284 return 0;
285 }
286
287 static int bbsnet_connect(int n)
288 {
289 int sock = -1;
290 int ret = 0;
291 int loop;
292 int error;
293 int sock_connected = 0;
294 int flags_sock = -1;
295 int flags_stdin = -1;
296 int flags_stdout = -1;
297 struct sockaddr_in sin;
298 char input_buf[LINE_BUFFER_LEN];
299 char output_buf[LINE_BUFFER_LEN];
300 int input_buf_len = 0;
301 int output_buf_len = 0;
302 int input_buf_offset = 0;
303 int output_buf_offset = 0;
304 char input_conv[LINE_BUFFER_LEN * 2];
305 char output_conv[LINE_BUFFER_LEN * 2];
306 int input_conv_len = 0;
307 int output_conv_len = 0;
308 int input_conv_offset = 0;
309 int output_conv_offset = 0;
310 iconv_t input_cd = (iconv_t)(-1);
311 iconv_t output_cd = (iconv_t)(-1);
312 char tocode[CHARSET_MAX_LEN + 20];
313
314 #ifdef HAVE_SYS_EPOLL_H
315 struct epoll_event ev, events[MAX_EVENTS];
316 int epollfd = -1;
317 #else
318 struct pollfd pfds[3];
319 #endif
320
321 int nfds;
322 int stdin_read_wait = 0;
323 int stdout_write_wait = 0;
324 int sock_read_wait = 0;
325 int sock_write_wait = 0;
326 struct addrinfo hints, *res = NULL;
327 int tos;
328 char remote_addr[INET_ADDRSTRLEN];
329 int remote_port;
330 char local_addr[INET_ADDRSTRLEN];
331 int local_port;
332 socklen_t sock_len;
333 time_t t_begin, t_used;
334 struct timespec ts_begin, ts_now;
335 int progress_last = 0;
336 int ch;
337 char remote_user[USERNAME_MAX_LEN + 1];
338 char remote_pass[PASSWORD_MAX_LEN + 1];
339 ssh_session outbound_session = NULL;
340 ssh_channel outbound_channel = NULL;
341 int ssh_process_config = 0;
342 int ssh_log_level = SSH_LOG_NOLOG;
343
344 if (user_online_update("BBS_NET") < 0)
345 {
346 log_error("user_online_update(BBS_NET) error");
347 }
348
349 if (bbsnet_conf[n].use_ssh)
350 {
351 clearscr();
352
353 if (!SSH_v2)
354 {
355 moveto(1, 1);
356 prints("只有在以SSH方式登陆本站时,才能使用SSH站点穿梭。");
357 press_any_key();
358 return 0;
359 }
360
361 moveto(1, 1);
362 prints("通过SSH方式连接[%s]...", bbsnet_conf[n].site_name);
363 moveto(2, 1);
364 prints("请输入用户名: ");
365 iflush();
366 if (str_input(remote_user, sizeof(remote_user), DOECHO) < 0)
367 {
368 return -1;
369 }
370 if (remote_user[0] == '\0')
371 {
372 return 0;
373 }
374
375 moveto(3, 1);
376 prints("请输入密码: ");
377 iflush();
378 if (str_input(remote_pass, sizeof(remote_pass), NOECHO) < 0)
379 {
380 return -1;
381 }
382 if (remote_pass[0] == '\0')
383 {
384 return 0;
385 }
386 }
387
388 clearscr();
389
390 moveto(1, 1);
391 prints("\033[1;32m正在测试往 %s (%s) 的连接,请稍候... \033[m\r\n",
392 bbsnet_conf[n].site_name, bbsnet_conf[n].host_name);
393 prints("\033[1;32m连接进行中,按\033[1;33mCtrl+C\033[1;32m中断。\033[m\r\n");
394 progress_bar(0, PROGRESS_BAR_LEN);
395
396 if (clock_gettime(CLOCK_REALTIME, &ts_begin) == -1)
397 {
398 log_error("clock_gettime() error (%d)", errno);
399 ret = -1;
400 goto cleanup;
401 }
402 ts_now = ts_begin;
403
404 memset(&hints, 0, sizeof(hints));
405 hints.ai_family = AF_INET;
406 hints.ai_socktype = SOCK_STREAM;
407 hints.ai_protocol = IPPROTO_TCP;
408
409 if ((ret = getaddrinfo(BBS_address, NULL, &hints, &res)) != 0)
410 {
411 log_error("getaddrinfo() error (%d): %s", ret, gai_strerror(ret));
412 ret = -1;
413 goto cleanup;
414 }
415
416 if (inet_ntop(AF_INET, &(((struct sockaddr_in *)res->ai_addr)->sin_addr), local_addr, sizeof(local_addr)) == NULL)
417 {
418 log_error("inet_ntop() error (%d)", errno);
419 ret = -1;
420 goto cleanup;
421 }
422 local_port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
423
424 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
425 if (sock < 0)
426 {
427 log_error("socket() error (%d)", errno);
428 ret = -1;
429 goto cleanup;
430 }
431
432 if (bind(sock, res->ai_addr, res->ai_addrlen) < 0)
433 {
434 log_error("bind(%s:%u) error (%d)", local_addr, local_port, errno);
435 ret = -1;
436 goto cleanup;
437 }
438
439 freeaddrinfo(res);
440 res = NULL;
441
442 memset(&hints, 0, sizeof(hints));
443 hints.ai_family = AF_INET;
444 hints.ai_flags = AI_NUMERICSERV;
445 hints.ai_socktype = SOCK_STREAM;
446 hints.ai_protocol = IPPROTO_TCP;
447
448 if ((ret = getaddrinfo(bbsnet_conf[n].host_name, bbsnet_conf[n].port, &hints, &res)) != 0)
449 {
450 log_error("getaddrinfo() error (%d): %s", ret, gai_strerror(ret));
451 prints("\033[1;31m查找主机名失败!\033[m\r\n");
452 press_any_key();
453 ret = -1;
454 goto cleanup;
455 }
456
457 if (inet_ntop(AF_INET, &(((struct sockaddr_in *)res->ai_addr)->sin_addr), remote_addr, sizeof(remote_addr)) == NULL)
458 {
459 log_error("inet_ntop() error (%d)", errno);
460 ret = -1;
461 goto cleanup;
462 }
463 remote_port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
464
465 // Set socket as non-blocking
466 if ((flags_sock = fcntl(sock, F_GETFL, 0)) == -1)
467 {
468 log_error("fcntl(F_GETFL) error (%d)", errno);
469 ret = -1;
470 goto cleanup;
471 }
472 if ((fcntl(sock, F_SETFL, flags_sock | O_NONBLOCK)) == -1)
473 {
474 log_error("fcntl(F_SETFL) error (%d)", errno);
475 ret = -1;
476 goto cleanup;
477 }
478
479 // Set STDIN/STDOUT as non-blocking
480 if ((flags_stdin = fcntl(STDIN_FILENO, F_GETFL, 0)) == -1)
481 {
482 log_error("fcntl(F_GETFL) error (%d)", errno);
483 ret = -1;
484 goto cleanup;
485 }
486 if ((flags_stdout = fcntl(STDOUT_FILENO, F_GETFL, 0)) == -1)
487 {
488 log_error("fcntl(F_GETFL) error (%d)", errno);
489 ret = -1;
490 goto cleanup;
491 }
492 if ((fcntl(STDIN_FILENO, F_SETFL, flags_stdin | O_NONBLOCK)) == -1)
493 {
494 log_error("fcntl(F_SETFL) error (%d)", errno);
495 ret = -1;
496 goto cleanup;
497 }
498 if ((fcntl(STDOUT_FILENO, F_SETFL, flags_stdout | O_NONBLOCK)) == -1)
499 {
500 log_error("fcntl(F_SETFL) error (%d)", errno);
501 ret = -1;
502 goto cleanup;
503 }
504
505 #ifdef HAVE_SYS_EPOLL_H
506 epollfd = epoll_create1(0);
507 if (epollfd < 0)
508 {
509 log_error("epoll_create1() error (%d)", errno);
510 ret = -1;
511 goto cleanup;
512 }
513
514 ev.events = EPOLLOUT | EPOLLET;
515 ev.data.fd = sock;
516 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev) == -1)
517 {
518 log_error("epoll_ctl(socket) error (%d)", errno);
519 ret = -1;
520 goto cleanup;
521 }
522
523 ev.events = EPOLLIN | EPOLLET;
524 ev.data.fd = STDIN_FILENO;
525 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
526 {
527 log_error("epoll_ctl(STDIN_FILENO) error (%d)", errno);
528 ret = -1;
529 goto cleanup;
530 }
531 #endif
532
533 while ((ts_now.tv_sec - ts_begin.tv_sec) * 1000 +
534 (ts_now.tv_nsec - ts_begin.tv_nsec) / 1000 / 1000 <
535 REMOTE_CONNECT_TIMEOUT * 1000 &&
536 !SYS_server_exit)
537 {
538 if (clock_gettime(CLOCK_REALTIME, &ts_now) == -1)
539 {
540 log_error("clock_gettime() error (%d)", errno);
541 ret = -1;
542 goto cleanup;
543 }
544
545 if ((ret = connect(sock, res->ai_addr, res->ai_addrlen)) == 0)
546 {
547 sock_connected = 1;
548 break;
549 }
550 else if (ret < 0)
551 {
552 if (errno == EAGAIN || errno == EALREADY || errno == EINPROGRESS)
553 {
554 // Use select / epoll to check writability of the socket,
555 // then use getsockopt to check the status of the socket.
556 // See man connect(2)
557 break;
558 }
559 else if (errno == EINTR)
560 {
561 }
562 else
563 {
564 log_error("connect(socket) error (%d)", errno);
565 prints("\033[1;31m连接失败!\033[m\r\n");
566 press_any_key();
567 ret = -1;
568 goto cleanup;
569 }
570
571 if (progress_update(&ts_begin, &ts_now,
572 REMOTE_CONNECT_TIMEOUT * 1000,
573 &progress_last, PROGRESS_BAR_LEN) < 0)
574 {
575 log_error("progress_update() error");
576 ret = -1;
577 goto cleanup;
578 }
579 }
580 }
581
582 while ((ts_now.tv_sec - ts_begin.tv_sec) * 1000 +
583 (ts_now.tv_nsec - ts_begin.tv_nsec) / 1000 / 1000 <
584 REMOTE_CONNECT_TIMEOUT * 1000 &&
585 !sock_connected && !SYS_server_exit)
586 {
587 if (clock_gettime(CLOCK_REALTIME, &ts_now) == -1)
588 {
589 log_error("clock_gettime() error (%d)", errno);
590 ret = -1;
591 goto cleanup;
592 }
593
594 #ifdef HAVE_SYS_EPOLL_H
595 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
596 ret = nfds;
597 #else
598 pfds[0].fd = sock;
599 pfds[0].events = POLLOUT;
600 pfds[1].fd = STDIN_FILENO;
601 pfds[1].events = POLLIN;
602 nfds = 2;
603 ret = poll(pfds, (nfds_t)nfds, 100); // 0.1 second
604 #endif
605
606 if (ret < 0)
607 {
608 if (errno != EINTR)
609 {
610 #ifdef HAVE_SYS_EPOLL_H
611 log_error("epoll_wait() error (%d)", errno);
612 #else
613 log_error("poll() error (%d)", errno);
614 #endif
615 break;
616 }
617 }
618 else if (ret == 0) // timeout
619 {
620 }
621 else // ret > 0
622 {
623 for (int i = 0; i < nfds; i++)
624 {
625 #ifdef HAVE_SYS_EPOLL_H
626 if (events[i].data.fd == sock)
627 #else
628 if (pfds[i].fd == sock && (pfds[i].revents & POLLOUT))
629 #endif
630 {
631 socklen_t len = sizeof(error);
632 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
633 {
634 log_error("getsockopt() error (%d) !", errno);
635 ret = -1;
636 goto cleanup;
637 }
638 if (error == 0)
639 {
640 sock_connected = 1;
641 break;
642 }
643 }
644 #ifdef HAVE_SYS_EPOLL_H
645 else if (events[i].data.fd == STDIN_FILENO)
646 #else
647 else if (pfds[i].fd == STDIN_FILENO && (pfds[i].revents & POLLIN))
648 #endif
649 {
650 do
651 {
652 ch = igetch(0);
653 } while (ch == 0);
654 if (ch == Ctrl('C') || ch == KEY_ESC)
655 {
656 ret = 0;
657 goto cleanup;
658 }
659 }
660 }
661 }
662
663 if (progress_update(&ts_begin, &ts_now,
664 REMOTE_CONNECT_TIMEOUT * 1000,
665 &progress_last, PROGRESS_BAR_LEN) < 0)
666 {
667 log_error("progress_update() error");
668 ret = -1;
669 goto cleanup;
670 }
671 }
672 if (SYS_server_exit)
673 {
674 ret = 0;
675 goto cleanup;
676 }
677 if (!sock_connected)
678 {
679 progress_bar(100, PROGRESS_BAR_LEN);
680 prints("\033[1;31m连接失败!\033[m\r\n");
681 press_any_key();
682 ret = -1;
683 goto cleanup;
684 }
685
686 tos = IPTOS_LOWDELAY;
687 if (setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
688 {
689 log_error("setsockopt IP_TOS=%d error (%d)", tos, errno);
690 }
691
692 sock_len = sizeof(sin);
693 if (getsockname(sock, (struct sockaddr *)&sin, &sock_len) < 0)
694 {
695 log_error("getsockname() error: %d", errno);
696 ret = -1;
697 goto cleanup;
698 }
699
700 if (inet_ntop(AF_INET, &(sin.sin_addr), local_addr, sizeof(local_addr)) == NULL)
701 {
702 log_error("inet_ntop() error (%d)", errno);
703 ret = -1;
704 goto cleanup;
705 }
706 local_port = ntohs(sin.sin_port);
707
708 if (bbsnet_conf[n].use_ssh)
709 {
710 outbound_session = ssh_new();
711 if (outbound_session == NULL)
712 {
713 log_error("ssh_new() error");
714 ret = -1;
715 goto cleanup;
716 }
717
718 if (ssh_options_set(outbound_session, SSH_OPTIONS_FD, &sock) < 0 ||
719 ssh_options_set(outbound_session, SSH_OPTIONS_PROCESS_CONFIG, &ssh_process_config) < 0 ||
720 ssh_options_set(outbound_session, SSH_OPTIONS_KNOWNHOSTS, SSH_KNOWN_HOSTS_FILE) < 0 ||
721 ssh_options_set(outbound_session, SSH_OPTIONS_HOST, bbsnet_conf[n].host_name) < 0 ||
722 ssh_options_set(outbound_session, SSH_OPTIONS_USER, remote_user) < 0 ||
723 ssh_options_set(outbound_session, SSH_OPTIONS_HOSTKEYS, "+ssh-ed25519,ecdsa-sha2-nistp256,ssh-rsa") < 0 ||
724 ssh_options_set(outbound_session, SSH_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0)
725 {
726 log_error("Error setting SSH options: %s", ssh_get_error(outbound_session));
727 ret = -1;
728 goto cleanup;
729 }
730
731 ssh_set_blocking(outbound_session, 0);
732
733 t_begin = time(NULL);
734 ret = SSH_ERROR;
735 while ((ts_now.tv_sec - ts_begin.tv_sec) * 1000 +
736 (ts_now.tv_nsec - ts_begin.tv_nsec) / 1000 / 1000 <
737 REMOTE_CONNECT_TIMEOUT * 1000 &&
738 !SYS_server_exit)
739 {
740 if (clock_gettime(CLOCK_REALTIME, &ts_now) == -1)
741 {
742 log_error("clock_gettime() error (%d)", errno);
743 ret = -1;
744 goto cleanup;
745 }
746
747 ret = ssh_connect(outbound_session);
748 if (ret == SSH_OK)
749 {
750 break;
751 }
752 else if (ret == SSH_AGAIN)
753 {
754 // log_debug("ssh_connect() error: SSH_AGAIN");
755 }
756 else // if (ret == SSH_ERROR)
757 {
758 log_error("ssh_connect() error: SSH_ERROR");
759 ret = -1;
760 goto cleanup;
761 }
762
763 if (progress_update(&ts_begin, &ts_now,
764 REMOTE_CONNECT_TIMEOUT * 1000,
765 &progress_last, PROGRESS_BAR_LEN) < 0)
766 {
767 log_error("progress_update() error");
768 ret = -1;
769 goto cleanup;
770 }
771 }
772 if (ret != SSH_OK)
773 {
774 progress_bar(100, PROGRESS_BAR_LEN);
775 prints("\033[1;31m连接超时!\033[m\r\n");
776 press_any_key();
777 ret = -1;
778 goto cleanup;
779 }
780
781 ret = ssh_session_is_known_server(outbound_session);
782 switch (ret)
783 {
784 case SSH_KNOWN_HOSTS_NOT_FOUND:
785 case SSH_KNOWN_HOSTS_UNKNOWN:
786 if (ssh_session_update_known_hosts(outbound_session) != SSH_OK)
787 {
788 log_error("ssh_session_update_known_hosts(%s) error", bbsnet_conf[n].host_name);
789 prints("\033[1;31m无法添加服务器证书\033[m\r\n");
790 press_any_key();
791 ret = -1;
792 goto cleanup;
793 }
794 log_common("SSH key of (%s) is added into %s", bbsnet_conf[n].host_name, SSH_KNOWN_HOSTS_FILE);
795 case SSH_KNOWN_HOSTS_OK:
796 break;
797 case SSH_KNOWN_HOSTS_CHANGED:
798 case SSH_KNOWN_HOSTS_OTHER:
799 log_error("ssh_session_is_known_server(%s) error: %d", bbsnet_conf[n].host_name, ret);
800 prints("\033[1;31m服务器证书已变更\033[m\r\n");
801 press_any_key();
802 ret = -1;
803 goto cleanup;
804 }
805
806 ret = SSH_AUTH_ERROR;
807 while ((ts_now.tv_sec - ts_begin.tv_sec) * 1000 +
808 (ts_now.tv_nsec - ts_begin.tv_nsec) / 1000 / 1000 <
809 REMOTE_CONNECT_TIMEOUT * 1000 &&
810 !SYS_server_exit)
811 {
812 if (clock_gettime(CLOCK_REALTIME, &ts_now) == -1)
813 {
814 log_error("clock_gettime() error (%d)", errno);
815 ret = -1;
816 goto cleanup;
817 }
818
819 ret = ssh_userauth_password(outbound_session, NULL, remote_pass);
820 if (ret == SSH_AUTH_SUCCESS)
821 {
822 break;
823 }
824 else if (ret == SSH_AUTH_AGAIN)
825 {
826 // log_debug("ssh_userauth_password() error: SSH_AUTH_AGAIN");
827 }
828 else if (ret == SSH_AUTH_ERROR)
829 {
830 log_error("ssh_userauth_password() error: SSH_AUTH_ERROR");
831 ret = -1;
832 goto cleanup;
833 }
834 else // if (ret == SSH_AUTH_DENIED)
835 {
836 log_debug("ssh_userauth_password() error: SSH_AUTH_DENIED");
837 prints("\033[1;31m身份验证失败!\033[m\r\n");
838 press_any_key();
839 ret = 0;
840 goto cleanup;
841 }
842
843 if (progress_update(&ts_begin, &ts_now,
844 REMOTE_CONNECT_TIMEOUT * 1000,
845 &progress_last, PROGRESS_BAR_LEN) < 0)
846 {
847 log_error("progress_update() error");
848 ret = -1;
849 goto cleanup;
850 }
851 }
852 if (ret != SSH_AUTH_SUCCESS)
853 {
854 progress_bar(100, PROGRESS_BAR_LEN);
855 prints("\033[1;31m连接超时!\033[m\r\n");
856 press_any_key();
857 ret = -1;
858 goto cleanup;
859 }
860
861 outbound_channel = ssh_channel_new(outbound_session);
862 if (outbound_channel == NULL)
863 {
864 log_error("ssh_channel_new() error");
865 ret = -1;
866 goto cleanup;
867 }
868
869 ret = SSH_ERROR;
870 while ((ts_now.tv_sec - ts_begin.tv_sec) * 1000 +
871 (ts_now.tv_nsec - ts_begin.tv_nsec) / 1000 / 1000 <
872 REMOTE_CONNECT_TIMEOUT * 1000 &&
873 !SYS_server_exit)
874 {
875 if (clock_gettime(CLOCK_REALTIME, &ts_now) == -1)
876 {
877 log_error("clock_gettime() error (%d)", errno);
878 ret = -1;
879 goto cleanup;
880 }
881
882 ret = ssh_channel_open_session(outbound_channel);
883 if (ret == SSH_OK)
884 {
885 break;
886 }
887 else if (ret == SSH_AGAIN)
888 {
889 // log_debug("ssh_channel_open_session() error: SSH_AGAIN");
890 }
891 else // if (ret == SSH_ERROR)
892 {
893 log_error("ssh_channel_open_session() error: SSH_ERROR");
894 ret = -1;
895 goto cleanup;
896 }
897
898 if (progress_update(&ts_begin, &ts_now,
899 REMOTE_CONNECT_TIMEOUT * 1000,
900 &progress_last, PROGRESS_BAR_LEN) < 0)
901 {
902 log_error("progress_update() error");
903 ret = -1;
904 goto cleanup;
905 }
906 }
907 if (ret != SSH_OK)
908 {
909 progress_bar(100, PROGRESS_BAR_LEN);
910 prints("\033[1;31m连接超时!\033[m\r\n");
911 press_any_key();
912 ret = -1;
913 goto cleanup;
914 }
915
916 ret = SSH_ERROR;
917 while ((ts_now.tv_sec - ts_begin.tv_sec) * 1000 +
918 (ts_now.tv_nsec - ts_begin.tv_nsec) / 1000 / 1000 <
919 REMOTE_CONNECT_TIMEOUT * 1000 &&
920 !SYS_server_exit)
921 {
922 if (clock_gettime(CLOCK_REALTIME, &ts_now) == -1)
923 {
924 log_error("clock_gettime() error (%d)", errno);
925 ret = -1;
926 goto cleanup;
927 }
928
929 ret = ssh_channel_request_pty(outbound_channel);
930 if (ret == SSH_OK)
931 {
932 break;
933 }
934 else if (ret == SSH_AGAIN)
935 {
936 // log_debug("ssh_channel_request_pty() error: SSH_AGAIN");
937 }
938 else // if (ret == SSH_ERROR)
939 {
940 log_error("ssh_channel_request_pty() error: SSH_ERROR");
941 ret = -1;
942 goto cleanup;
943 }
944
945 if (progress_update(&ts_begin, &ts_now,
946 REMOTE_CONNECT_TIMEOUT * 1000,
947 &progress_last, PROGRESS_BAR_LEN) < 0)
948 {
949 log_error("progress_update() error");
950 ret = -1;
951 goto cleanup;
952 }
953 }
954 if (ret != SSH_OK)
955 {
956 progress_bar(100, PROGRESS_BAR_LEN);
957 prints("\033[1;31m连接超时!\033[m\r\n");
958 press_any_key();
959 ret = -1;
960 goto cleanup;
961 }
962
963 ret = SSH_ERROR;
964 while ((ts_now.tv_sec - ts_begin.tv_sec) * 1000 +
965 (ts_now.tv_nsec - ts_begin.tv_nsec) / 1000 / 1000 <
966 REMOTE_CONNECT_TIMEOUT * 1000 &&
967 !SYS_server_exit)
968 {
969 if (clock_gettime(CLOCK_REALTIME, &ts_now) == -1)
970 {
971 log_error("clock_gettime() error (%d)", errno);
972 ret = -1;
973 goto cleanup;
974 }
975
976 ret = ssh_channel_request_shell(outbound_channel);
977 if (ret == SSH_OK)
978 {
979 break;
980 }
981 else if (ret == SSH_AGAIN)
982 {
983 // log_debug("ssh_channel_request_shell() error: SSH_AGAIN");
984 }
985 else // if (ret == SSH_ERROR)
986 {
987 log_error("ssh_channel_request_shell() error: SSH_ERROR");
988 ret = -1;
989 goto cleanup;
990 }
991
992 if (progress_update(&ts_begin, &ts_now,
993 REMOTE_CONNECT_TIMEOUT * 1000,
994 &progress_last, PROGRESS_BAR_LEN) < 0)
995 {
996 log_error("progress_update() error");
997 ret = -1;
998 goto cleanup;
999 }
1000 }
1001 if (ret != SSH_OK)
1002 {
1003 progress_bar(100, PROGRESS_BAR_LEN);
1004 prints("\033[1;31m连接超时!\033[m\r\n");
1005 press_any_key();
1006 ret = -1;
1007 goto cleanup;
1008 }
1009 }
1010
1011 prints("\033[1;31m连接成功!\033[m\r\n");
1012 iflush();
1013 log_common("BBSNET connect to %s:%d from %s:%d by [%s]",
1014 remote_addr, remote_port, local_addr, local_port, BBS_username);
1015
1016 snprintf(tocode, sizeof(tocode), "%s%s", bbsnet_conf[n].charset,
1017 (strcasecmp(stdio_charset, bbsnet_conf[n].charset) == 0 ? "" : "//IGNORE"));
1018 input_cd = iconv_open(tocode, stdio_charset);
1019 if (input_cd == (iconv_t)(-1))
1020 {
1021 log_error("iconv_open(%s->%s) error: %d", stdio_charset, tocode, errno);
1022 ret = -1;
1023 goto cleanup;
1024 }
1025
1026 snprintf(tocode, sizeof(tocode), "%s%s", stdio_charset,
1027 (strcasecmp(bbsnet_conf[n].charset, stdio_charset) == 0 ? "" : "//TRANSLIT"));
1028 output_cd = iconv_open(tocode, bbsnet_conf[n].charset);
1029 if (output_cd == (iconv_t)(-1))
1030 {
1031 log_error("iconv_open(%s->%s) error: %d", bbsnet_conf[n].charset, tocode, errno);
1032 ret = -1;
1033 goto cleanup;
1034 }
1035
1036 #ifdef HAVE_SYS_EPOLL_H
1037 ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
1038 ev.data.fd = sock;
1039 if (epoll_ctl(epollfd, EPOLL_CTL_MOD, sock, &ev) == -1)
1040 {
1041 log_error("epoll_ctl(socket) error (%d)", errno);
1042 ret = -1;
1043 goto cleanup;
1044 }
1045
1046 ev.events = EPOLLOUT | EPOLLET;
1047 ev.data.fd = STDOUT_FILENO;
1048 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1)
1049 {
1050 log_error("epoll_ctl(STDOUT_FILENO) error (%d)", errno);
1051 ret = -1;
1052 goto cleanup;
1053 }
1054 #endif
1055
1056 BBS_last_access_tm = t_begin = time(NULL);
1057 loop = 1;
1058
1059 while (loop && !SYS_server_exit)
1060 {
1061 if (SSH_v2 && ssh_channel_is_closed(SSH_channel))
1062 {
1063 log_debug("SSH channel is closed");
1064 loop = 0;
1065 break;
1066 }
1067
1068 if (bbsnet_conf[n].use_ssh && ssh_channel_is_closed(outbound_channel))
1069 {
1070 log_debug("Outbound channel is closed");
1071 loop = 0;
1072 break;
1073 }
1074
1075 #ifdef HAVE_SYS_EPOLL_H
1076 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
1077 ret = nfds;
1078 #else
1079 pfds[0].fd = STDIN_FILENO;
1080 pfds[0].events = POLLIN;
1081 pfds[1].fd = sock;
1082 pfds[1].events = POLLIN | POLLOUT;
1083 pfds[2].fd = STDOUT_FILENO;
1084 pfds[2].events = POLLOUT;
1085 nfds = 3;
1086 ret = poll(pfds, (nfds_t)nfds, 100); // 0.1 second
1087 #endif
1088
1089 if (ret < 0)
1090 {
1091 if (errno != EINTR)
1092 {
1093 #ifdef HAVE_SYS_EPOLL_H
1094 log_error("epoll_wait() error (%d)", errno);
1095 #else
1096 log_error("poll() error (%d)", errno);
1097 #endif
1098 break;
1099 }
1100 continue;
1101 }
1102 else if (ret == 0) // timeout
1103 {
1104 if (time(NULL) - BBS_last_access_tm >= BBS_max_user_idle_time)
1105 {
1106 log_debug("User input timeout");
1107 break;
1108 }
1109 }
1110
1111 for (int i = 0; i < nfds; i++)
1112 {
1113 #ifdef HAVE_SYS_EPOLL_H
1114 if (events[i].events & (EPOLLHUP | EPOLLERR))
1115 #else
1116 if (pfds[i].revents & (POLLHUP | POLLERR))
1117 #endif
1118 {
1119 #ifdef HAVE_SYS_EPOLL_H
1120 log_debug("FD (%d) error events (%d)", events[i].data.fd, events[i].events);
1121 #else
1122 log_debug("FD (%d) error events (%d)", pfds[i].fd, pfds[i].revents);
1123 #endif
1124 loop = 0;
1125 break;
1126 }
1127
1128 #ifdef HAVE_SYS_EPOLL_H
1129 if (events[i].data.fd == STDIN_FILENO && (events[i].events & EPOLLIN))
1130 #else
1131 if (pfds[i].fd == STDIN_FILENO && (pfds[i].revents & POLLIN))
1132 #endif
1133 {
1134 stdin_read_wait = 1;
1135 }
1136
1137 #ifdef HAVE_SYS_EPOLL_H
1138 if (events[i].data.fd == sock)
1139 #else
1140 if (pfds[i].fd == sock)
1141 #endif
1142 {
1143 #ifdef HAVE_SYS_EPOLL_H
1144 if (events[i].events & EPOLLIN)
1145 #else
1146 if (pfds[i].revents & POLLIN)
1147 #endif
1148 {
1149 sock_read_wait = 1;
1150 }
1151
1152 #ifdef HAVE_SYS_EPOLL_H
1153 if (events[i].events & EPOLLOUT)
1154 #else
1155 if (pfds[i].revents & POLLOUT)
1156 #endif
1157 {
1158 sock_write_wait = 1;
1159 }
1160 }
1161
1162 #ifdef HAVE_SYS_EPOLL_H
1163 if (events[i].data.fd == STDOUT_FILENO && (events[i].events & EPOLLOUT))
1164 #else
1165 if (pfds[i].fd == STDOUT_FILENO && (pfds[i].revents & POLLOUT))
1166 #endif
1167 {
1168 stdout_write_wait = 1;
1169 }
1170 }
1171
1172 if (stdin_read_wait)
1173 {
1174 while (input_buf_len < sizeof(input_buf) && !SYS_server_exit)
1175 {
1176 if (SSH_v2)
1177 {
1178 ret = ssh_channel_read_nonblocking(SSH_channel, input_buf + input_buf_len, sizeof(input_buf) - (uint32_t)input_buf_len, 0);
1179 if (ret == SSH_ERROR)
1180 {
1181 log_debug("ssh_channel_read_nonblocking() error: %s", ssh_get_error(SSH_session));
1182 loop = 0;
1183 break;
1184 }
1185 else if (ret == SSH_EOF)
1186 {
1187 stdin_read_wait = 0;
1188 loop = 0;
1189 break;
1190 }
1191 else if (ret == 0)
1192 {
1193 // Send NO-OP to remote server
1194 input_buf[input_buf_len] = '\0';
1195 input_buf_len++;
1196
1197 BBS_last_access_tm = time(NULL);
1198 stdin_read_wait = 0;
1199 break; // Check whether channel is still open
1200 }
1201 }
1202 else
1203 {
1204 ret = (int)read(STDIN_FILENO, input_buf + input_buf_len, sizeof(input_buf) - (size_t)input_buf_len);
1205 }
1206 if (ret < 0)
1207 {
1208 if (errno == EAGAIN || errno == EWOULDBLOCK)
1209 {
1210 stdin_read_wait = 0;
1211 break;
1212 }
1213 else if (errno == EINTR)
1214 {
1215 continue;
1216 }
1217 else
1218 {
1219 log_error("read(STDIN) error (%d)", errno);
1220 loop = 0;
1221 break;
1222 }
1223 }
1224 else if (ret == 0) // broken pipe
1225 {
1226 log_debug("read(STDIN) EOF");
1227 stdin_read_wait = 0;
1228 loop = 0;
1229 break;
1230 }
1231 else
1232 {
1233 input_buf_len += ret;
1234 BBS_last_access_tm = time(NULL);
1235
1236 // Refresh current action while user input
1237 if (user_online_update("BBS_NET") < 0)
1238 {
1239 log_error("user_online_update(BBS_NET) error");
1240 }
1241
1242 continue;
1243 }
1244 }
1245 }
1246
1247 if (sock_write_wait)
1248 {
1249 if (input_buf_offset < input_buf_len)
1250 {
1251 // For debug
1252 #ifdef _DEBUG
1253 for (int j = input_buf_offset; j < input_buf_len; j++)
1254 {
1255 log_debug("input: <--[%u]", (unsigned char)(input_buf[j]));
1256 }
1257 #endif
1258
1259 ret = io_buf_conv(input_cd, input_buf, &input_buf_len, &input_buf_offset, input_conv, sizeof(input_conv), &input_conv_len);
1260 if (ret < 0)
1261 {
1262 log_error("io_buf_conv(input, %d, %d, %d) error", input_buf_len, input_buf_offset, input_conv_len);
1263 input_buf_len = input_buf_offset; // Discard invalid sequence
1264 }
1265
1266 // For debug
1267 #ifdef _DEBUG
1268 for (int j = input_conv_offset; j < input_conv_len; j++)
1269 {
1270 log_debug("input_conv: <--[%u]", (unsigned char)(input_conv[j]));
1271 }
1272 #endif
1273 }
1274
1275 while (input_conv_offset < input_conv_len && !SYS_server_exit)
1276 {
1277 if (bbsnet_conf[n].use_ssh)
1278 {
1279 ret = ssh_channel_write(outbound_channel, input_conv + input_conv_offset, (uint32_t)(input_conv_len - input_conv_offset));
1280 if (ret == SSH_ERROR)
1281 {
1282 log_debug("ssh_channel_write() error: %s", ssh_get_error(outbound_session));
1283 loop = 0;
1284 break;
1285 }
1286 }
1287 else
1288 {
1289 ret = (int)write(sock, input_conv + input_conv_offset, (size_t)(input_conv_len - input_conv_offset));
1290 }
1291 if (ret < 0)
1292 {
1293 if (errno == EAGAIN || errno == EWOULDBLOCK)
1294 {
1295 sock_write_wait = 0;
1296 break;
1297 }
1298 else if (errno == EINTR)
1299 {
1300 continue;
1301 }
1302 else
1303 {
1304 log_debug("write(socket) error (%d)", errno);
1305 loop = 0;
1306 break;
1307 }
1308 }
1309 else if (ret == 0) // broken pipe
1310 {
1311 log_debug("write(socket) EOF");
1312 sock_write_wait = 0;
1313 loop = 0;
1314 break;
1315 }
1316 else
1317 {
1318 input_conv_offset += ret;
1319 if (input_conv_offset >= input_conv_len) // Output buffer complete
1320 {
1321 input_conv_offset = 0;
1322 input_conv_len = 0;
1323 break;
1324 }
1325 continue;
1326 }
1327 }
1328 }
1329
1330 if (sock_read_wait)
1331 {
1332 while (output_buf_len < sizeof(output_buf) && !SYS_server_exit)
1333 {
1334 if (bbsnet_conf[n].use_ssh)
1335 {
1336 ret = ssh_channel_read_nonblocking(outbound_channel, output_buf + output_buf_len,
1337 (uint32_t)(sizeof(output_buf) - (size_t)output_buf_len), 0);
1338 if (ret == SSH_ERROR)
1339 {
1340 log_debug("ssh_channel_read_nonblocking() error: %s", ssh_get_error(outbound_session));
1341 loop = 0;
1342 break;
1343 }
1344 else if (ret == SSH_EOF)
1345 {
1346 sock_read_wait = 0;
1347 loop = 0;
1348 break;
1349 }
1350 else if (ret == 0)
1351 {
1352 sock_read_wait = 0;
1353 break;
1354 }
1355 }
1356 else
1357 {
1358 ret = (int)read(sock, output_buf + output_buf_len, sizeof(output_buf) - (size_t)output_buf_len);
1359 }
1360 if (ret < 0)
1361 {
1362 if (errno == EAGAIN || errno == EWOULDBLOCK)
1363 {
1364 sock_read_wait = 0;
1365 break;
1366 }
1367 else if (errno == EINTR)
1368 {
1369 continue;
1370 }
1371 else
1372 {
1373 log_debug("read(socket) error (%d)", errno);
1374 loop = 0;
1375 break;
1376 }
1377 }
1378 else if (ret == 0) // broken pipe
1379 {
1380 log_debug("read(socket) EOF");
1381 sock_read_wait = 0;
1382 loop = 0;
1383 break;
1384 }
1385 else
1386 {
1387 output_buf_len += ret;
1388 continue;
1389 }
1390 }
1391 }
1392
1393 if (stdout_write_wait)
1394 {
1395 if (output_buf_offset < output_buf_len)
1396 {
1397 ret = io_buf_conv(output_cd, output_buf, &output_buf_len, &output_buf_offset, output_conv, sizeof(output_conv), &output_conv_len);
1398 if (ret < 0)
1399 {
1400 log_error("io_buf_conv(output, %d, %d, %d) error", output_buf_len, output_buf_offset, output_conv_len);
1401 output_buf_len = output_buf_offset; // Discard invalid sequence
1402 }
1403 }
1404
1405 while (output_conv_offset < output_conv_len && !SYS_server_exit)
1406 {
1407 if (SSH_v2)
1408 {
1409 ret = ssh_channel_write(SSH_channel, output_conv + output_conv_offset, (uint32_t)(output_conv_len - output_conv_offset));
1410 if (ret == SSH_ERROR)
1411 {
1412 log_debug("ssh_channel_write() error: %s", ssh_get_error(SSH_session));
1413 loop = 0;
1414 break;
1415 }
1416 }
1417 else
1418 {
1419 ret = (int)write(STDOUT_FILENO, output_conv + output_conv_offset, (size_t)(output_conv_len - output_conv_offset));
1420 }
1421 if (ret < 0)
1422 {
1423 if (errno == EAGAIN || errno == EWOULDBLOCK)
1424 {
1425 stdout_write_wait = 0;
1426 break;
1427 }
1428 else if (errno == EINTR)
1429 {
1430 continue;
1431 }
1432 else
1433 {
1434 log_debug("write(STDOUT) error (%d)", errno);
1435 loop = 0;
1436 break;
1437 }
1438 }
1439 else if (ret == 0) // broken pipe
1440 {
1441 log_debug("write(STDOUT) EOF");
1442 stdout_write_wait = 0;
1443 loop = 0;
1444 break;
1445 }
1446 else
1447 {
1448 output_conv_offset += ret;
1449 if (output_conv_offset >= output_conv_len) // Output buffer complete
1450 {
1451 output_conv_offset = 0;
1452 output_conv_len = 0;
1453 break;
1454 }
1455 continue;
1456 }
1457 }
1458 }
1459 }
1460
1461 ret = 1; // Normal disconnect
1462 BBS_last_access_tm = time(NULL);
1463 t_used = BBS_last_access_tm - t_begin;
1464 log_common("BBSNET disconnect, %ld days %ld hours %ld minutes %ld seconds used",
1465 t_used / 86400, t_used % 86400 / 3600, t_used % 3600 / 60, t_used % 60);
1466
1467 cleanup:
1468 // Clear sensitive data
1469 memset(remote_pass, 0, sizeof(remote_pass));
1470 memset(remote_user, 0, sizeof(remote_user));
1471
1472 if (input_cd != (iconv_t)(-1))
1473 {
1474 iconv_close(input_cd);
1475 }
1476 if (output_cd != (iconv_t)(-1))
1477 {
1478 iconv_close(output_cd);
1479 }
1480
1481 #ifdef HAVE_SYS_EPOLL_H
1482 if (epollfd != -1 && close(epollfd) < 0)
1483 {
1484 log_error("close(epoll) error (%d)");
1485 }
1486 #endif
1487
1488 if (bbsnet_conf[n].use_ssh)
1489 {
1490 if (outbound_channel != NULL)
1491 {
1492 ssh_channel_send_eof(outbound_channel);
1493 ssh_channel_close(outbound_channel);
1494 ssh_channel_free(outbound_channel);
1495 }
1496 if (outbound_session != NULL)
1497 {
1498 ssh_disconnect(outbound_session);
1499 ssh_free(outbound_session);
1500 }
1501 }
1502
1503 // Restore STDIN/STDOUT flags
1504 if (flags_stdin != -1 && fcntl(STDIN_FILENO, F_SETFL, flags_stdin) == -1)
1505 {
1506 log_error("fcntl(F_SETFL) error (%d)", errno);
1507 }
1508 if (flags_stdout != -1 && fcntl(STDOUT_FILENO, F_SETFL, flags_stdout) == -1)
1509 {
1510 log_error("fcntl(F_SETFL) error (%d)", errno);
1511 }
1512
1513 if (sock != -1 && close(sock) == -1)
1514 {
1515 log_error("close(socket) error (%d)", errno);
1516 }
1517
1518 if (res)
1519 {
1520 freeaddrinfo(res);
1521 }
1522
1523 return ret;
1524 }
1525
1526 static int bbsnet_refresh()
1527 {
1528 clearscr();
1529
1530 moveto(1, 1);
1531 prints(" ------------------------------------------------------------------------------ ");
1532 for (int i = 2; i < 19; i++)
1533 {
1534 moveto(i, 1);
1535 prints("|");
1536 moveto(i, 80);
1537 prints("|");
1538 }
1539 moveto(19, 1);
1540 prints("|------------------------------------------------------------------------------|");
1541 moveto(22, 1);
1542 prints(" ------------------------------------------------------------------------------ ");
1543 moveto(23, 1);
1544 prints(" [\033[1;32mCtrl+C\033[m]退出");
1545
1546 iflush();
1547
1548 return 0;
1549 }
1550
1551 static int bbsnet_selchange()
1552 {
1553 int i = bbsnet_menu.menu_item_pos[0];
1554
1555 moveto(20, 1);
1556 clrtoeol();
1557 prints("|\033[1m单位: \033[1;33m%s\033[m%*s 站名: \033[1;33m%s\033[m%*s 类型: \033[1;33m%s\033[m",
1558 bbsnet_conf[i].org_name, 20 - str_length(bbsnet_conf[i].org_name, 1), "",
1559 bbsnet_conf[i].site_name, 20 - str_length(bbsnet_conf[i].site_name, 1), "",
1560 (bbsnet_conf[i].use_ssh ? "SSH" : "Telnet"));
1561 moveto(20, 80);
1562 prints("|");
1563 moveto(21, 1);
1564 clrtoeol();
1565 prints("|\033[1m连往: \033[1;33m%-20s\033[m 端口: \033[1;33m%-5s\033[m 编码: \033[1;33m%s\033[m",
1566 bbsnet_conf[i].host_name, bbsnet_conf[i].port, bbsnet_conf[i].charset);
1567 moveto(21, 80);
1568 prints("|");
1569 iflush();
1570
1571 return 0;
1572 }
1573
1574 int bbs_net()
1575 {
1576 int ch;
1577
1578 if (load_bbsnet_conf(CONF_BBSNET) < 0)
1579 {
1580 clearscr();
1581 moveto(1, 1);
1582 prints("加载穿梭配置失败!");
1583 press_any_key();
1584 return -1;
1585 }
1586
1587 bbsnet_refresh();
1588 display_menu(&bbsnet_menu);
1589 bbsnet_selchange();
1590
1591 while (!SYS_server_exit)
1592 {
1593 ch = igetch(100);
1594
1595 if (ch != KEY_NULL && ch != KEY_TIMEOUT)
1596 {
1597 BBS_last_access_tm = time(NULL);
1598 }
1599
1600 switch (ch)
1601 {
1602 case KEY_NULL: // broken pipe
1603 log_debug("KEY_NULL");
1604 goto cleanup;
1605 case KEY_TIMEOUT:
1606 if (time(NULL) - BBS_last_access_tm >= BBS_max_user_idle_time)
1607 {
1608 log_debug("User input timeout");
1609 goto cleanup;
1610 }
1611 continue;
1612 case KEY_ESC:
1613 case Ctrl('C'): // user cancel
1614 goto cleanup;
1615 case CR:
1616 if (bbsnet_connect(bbsnet_menu.menu_item_pos[0]) < 0)
1617 {
1618 log_debug("bbsnet_connect() error");
1619 }
1620 // Force cleanup anything remaining in the output buffer
1621 clearscr();
1622 iflush();
1623 // Clear screen and redraw menu
1624 bbsnet_refresh();
1625 display_menu(&bbsnet_menu);
1626 bbsnet_selchange();
1627 break;
1628 case KEY_UP:
1629 for (int i = 0; i < STATION_PER_LINE; i++)
1630 {
1631 menu_control(&bbsnet_menu, KEY_UP);
1632 }
1633 bbsnet_selchange();
1634 break;
1635 case KEY_DOWN:
1636 for (int i = 0; i < STATION_PER_LINE; i++)
1637 {
1638 menu_control(&bbsnet_menu, KEY_DOWN);
1639 }
1640 bbsnet_selchange();
1641 break;
1642 case KEY_LEFT:
1643 menu_control(&bbsnet_menu, KEY_UP);
1644 bbsnet_selchange();
1645 break;
1646 case KEY_RIGHT:
1647 menu_control(&bbsnet_menu, KEY_DOWN);
1648 bbsnet_selchange();
1649 break;
1650 case KEY_HOME:
1651 case KEY_PGUP:
1652 menu_control(&bbsnet_menu, KEY_PGUP);
1653 bbsnet_selchange();
1654 break;
1655 case KEY_END:
1656 case KEY_PGDN:
1657 menu_control(&bbsnet_menu, KEY_PGDN);
1658 bbsnet_selchange();
1659 break;
1660 default:
1661 menu_control(&bbsnet_menu, ch);
1662 bbsnet_selchange();
1663 break;
1664 }
1665 }
1666
1667 cleanup:
1668 unload_bbsnet_conf();
1669
1670 return 0;
1671 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1