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