1 /* VER 158 TAB P $Id: telnet.c,v 1.9.2.4 2003/01/21 09:47:25 egil Exp $
2 *
3 * implement a very simple telnet protocol
4 *
5 * copyright 1996, 1997 Egil Kvaleberg, egil@kvaleberg.no
6 * the GNU General Public License applies
7 *
8 * $Log: telnet.c,v $
9 * Revision 1.9.2.4 2003/01/21 09:47:25 egil
10 * Renamed MAXHEADERSIZE to MAX_HEADER_SIZE due to name collision
11 *
12 * Revision 1.9.2.3 2002/10/01 08:54:59 egil
13 * Changed sprintf to snprintf
14 *
15 * Revision 1.9.2.2 2002/09/21 17:20:36 egil
16 * A long range of patches incorporated..
17 *
18 * Revision 1.9.2.1 2002/01/29 06:44:48 egil
19 * Changing from xmalloc, xrealloc, xstrcpy to
20 * malloc_perfect, realloc_perfect and strdup_perfect
21 *
22 * Revision 1.9 1999/03/31 05:53:46 src
23 * Seperated MAXHEADERSIZE from NNTP_STRLEN
24 *
25 * Revision 1.8 1999/03/31 03:38:29 src
26 * Moved errno.h to common.h
27 *
28 * Revision 1.7 1998/09/09 07:32:14 src
29 * Version 1.1
30 *
31 * Revision 1.6 1998/09/03 02:49:31 src
32 * Fixed stuff detected by -Wall
33 *
34 * Revision 1.5 1998/07/12 09:39:30 src
35 * newsx version 1.0
36 */
37
38 #include "common.h"
39 #include "proto.h"
40 #include "nntp.h"
41 #include "news.h" /* MAX_HEADER_SIZE */
42
43 #include <signal.h>
44 #include <setjmp.h>
45
46 #include <netdb.h>
47 #include <sys/socket.h>
48
49 #define TELOPTS
50 #define TELCMDS
51 #include <arpa/telnet.h>
52
53 #ifndef IAC
54 #define IAC 255
55 #define DONT 254
56 #define DO 253
57 #define WONT 252
58 #define WILL 251
59 #define TELOPT_BINARY 0
60 #define TELOPT_ECHO 1
61 #define TELOPT_SGA 3
62 #define TELOPT_LINEMODE 34
63 #endif
64
65 /*
66 * write line with telnet protocol
67 * suppress echo
68 * return false if a fatal error occurred
69 */
70 int
write_telnet_line(char * line,int len,SOCKET_D * sock)71 write_telnet_line(char *line, int len, SOCKET_D *sock)
72 {
73 int n;
74 char c1,c2;
75 char echo[MAX_HEADER_SIZE+3];
76 int wrong_echo = 0;
77
78 /* BUG: should escape IACs */
79 /* BUG: should we use flag MSG_DONTWAIT? */
80 if (send(sock->w_fd,line,len,0) != len
81 || send(sock->w_fd,"\r",1,0) != 1) { /* using CR as newline for telnet */
82 log_msg(L_ERRno,"write error on telnet socket");
83 return 0;
84 }
85 if (sock->is_echo) {
86 again:
87 /* await echo */
88 echo[MAX_HEADER_SIZE+2] = '\0';
89 if (read_telnet(echo,MAX_HEADER_SIZE+2,sock) < 0) return 0;
90 for (n=0; n<len; ++n) {
91 c1 = line[n];
92 c2 = echo[n];
93 if ((c1=='\r' || c1=='\n')
94 && (c2=='\r' || c2=='\n' || !c2)) break;
95
96 /* NOTE: echo may be filtered through 7bit mask */
97 if ((line[n] & 0x7f) != (echo[n] & 0x7f)) {
98 log_msg(L_DEBUG,"wrong echo \"%s\"",echo);
99 if (++wrong_echo >= 100) {
100 /* prevent us hanging here forever... */
101 log_msg(L_ERR,"persistent wrong echo: \"%s\"", echo);
102 exit_cleanup(11);
103 }
104 goto again;
105 }
106 }
107 }
108 return 1;
109 }
110
111 /*
112 * read line with telnet protocol
113 * NOTE: this is really a primitive implementation!
114 */
115 static void
send_iac3(SOCKET_D * sock,char how,char what)116 send_iac3(SOCKET_D *sock,char how,char what)
117 {
118 char sbuf[3];
119 sbuf[0] = IAC;
120 sbuf[1] = how;
121 sbuf[2] = what;
122 send(sock->w_fd,sbuf,3,0);
123 }
124
125 /*
126 * return string for telnet command
127 */
128 static char *
str_telcmd(int cmd)129 str_telcmd(int cmd)
130 {
131 static char str[10];
132 #ifdef TELCMD
133 if (TELCMD_OK(cmd)) {
134 return TELCMD(cmd);
135 }
136 #endif
137 snprintf(str,sizeof(str),"%d",cmd);
138 return str;
139 }
140
141 /*
142 * return string for telnet option
143 */
144 static char *
str_telopt(int opt)145 str_telopt(int opt)
146 {
147 static char str[10];
148 #ifdef TELOPT
149 if (TELOPT_OK(opt)) {
150 return TELOPT(opt);
151 }
152 #endif
153 snprintf(str,sizeof(str),"%d",opt);
154 return str;
155 }
156
157 /*
158 * read line with telnet protocol
159 * return string length, negative if a fatal error occurred
160 * tries to make sense out of various CR-LF peculiarities
161 * NOTE: this is really a primitive implementation!
162 */
163 int
read_telnet(char * line,int size,SOCKET_D * sock)164 read_telnet(char *line, int size, SOCKET_D *sock)
165 {
166 int ix;
167 int state;
168 int c;
169 int d;
170 int is_cr = 0;
171 static unsigned char *buf = 0;
172 static int buf_n = 0;
173 static int buf_m = 0;
174 static int not_first_time = 0;
175
176 if (!buf) buf = malloc_perfect(MAX_HEADER_SIZE);
177
178 if (!not_first_time) {
179 not_first_time = 1;
180 /* IAC DO TELOPT_BINARY */
181 /* IAC DO TELOPT_SGA = suppress goahead */
182 /* IAC DONT TELOPT_ECHO (which is default) */
183 /*
184 log_msg(L_DEBUGMORE,"telnet send: IAC DO BINARY, IAC DO SGA");
185 send_iac3(sock,DO, TELOPT_BINARY);
186 send_iac3(sock,DO, TELOPT_SGA);
187 */
188 /*
189 send_iac3(sock,DONT,TELOPT_ECHO);
190 send_iac3(sock,DONT,TELOPT_LINEMODE);
191 */
192 }
193 ix = 0;
194 state = 0;
195 while (ix < size) {
196 /* read a packet */
197 while (buf_n >= buf_m) {
198 /* need to refill local buffer */
199 buf_n = 0;
200 buf_m = recv(sock->r_fd,buf,MAX_HEADER_SIZE,0);
201 if (buf_m < 0) {
202 log_msg(L_ERRno,"read error on telnet socket");
203 return -1;
204 }
205 if (buf_m == 0) {
206 /* [ver 0.10] there is nothing more... */
207 line[ix] = '\0';
208 return ix;
209 }
210 }
211 c = buf[buf_n++];
212
213 switch (state) {
214 default:
215 if (c == IAC) {
216 state = IAC;
217 } else if (c == '\0') {
218 /* ignore */
219 #if 0
220 } else if (ix == 0 && c == '\r') {
221 /* ignore */
222 #endif
223 } else {
224 got_ch:
225 #if 1
226 if (is_cr && c != '\n') {
227 /* carriage return by itself */
228 --buf_n; /* push back character */
229 c = '\n'; /* and fake a newline too */
230 }
231 #endif
232 line[ix++] = c;
233 if (ix >= size) return ix;
234 is_cr = (c == '\r');
235 line[ix] = '\0';
236
237 /* NOTE: update for every new character */
238 if (sock->f_chat)
239 chat_update(line,sock);
240
241 if (c == '\n') {
242 return ix; /* end of line */
243 }
244 }
245 break;
246 case IAC:
247 switch (c) {
248 case IAC:
249 state = 0;
250 goto got_ch;
251 case DO:
252 case DONT:
253 case WILL:
254 case WONT:
255 state = c;
256 break;
257 case GA: /* go-ahead */
258 default:
259 /* ignore two characters... */
260 log_msg(L_DEBUGMORE,"telnet ignore: IAC %d",
261 str_telcmd(c));
262 state = 0;
263 break;
264 }
265 break;
266 case DO:
267 switch (c) {
268 case TELOPT_SGA:
269 case TELOPT_BINARY:
270 send_iac3(sock,d=WILL,c);
271 break;
272 case TELOPT_ECHO:
273 default:
274 send_iac3(sock,d=WONT,c);
275 break;
276 }
277 log_msg(L_DEBUGMORE,"telnet %s: IAC DO %s",
278 d==WILL ? "will":"wont",
279 str_telopt(c));
280 state = 0;
281 break;
282 case DONT:
283 log_msg(L_DEBUGMORE,"telnet ignore: IAC DONT %s",
284 str_telopt(c));
285 state = 0;
286 break;
287 case WILL:
288 case WONT:
289 log_msg(L_DEBUGMORE,"telnet ignore: IAC %s %s",
290 str_telcmd(state),
291 str_telopt(c));
292 /* ignore three characters... */
293 state = 0;
294 break;
295 }
296 }
297 return ix;
298 }
299