1 /*
2  *	binkleyforce -- unix FTN mailer project
3  *
4  *	Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU General Public License as published by
8  *	the Free Software Foundation; either version 2 of the License, or
9  *	(at your option) any later version.
10  *
11  *	$Id: prot_yoohoo.c,v 1.1.1.1 2004/09/09 09:52:39 kstepanenkov Exp $
12  */
13 
14 #include "includes.h"
15 #include "confread.h"
16 #include "logger.h"
17 #include "util.h"
18 #include "session.h"
19 #include "prot_yoohoo.h"
20 
21 typedef enum {
22 	YS_Init,
23 	YS_SendHello,
24 	YS_WaitResp,
25 	YS_Done,
26 	YS_Error
27 } YooHoo_SendState;
28 
29 typedef enum {
30 	YR_Init,
31 	YR_SendENQ,
32 	YR_WaitHeader,
33 	YR_TossJunk,
34 	YR_RecvHello,
35 	YR_SendNAK,
36 	YR_SendACK,
37 	YR_Done,
38 	YR_Error
39 } YooHoo_RecvState;
40 
yoohoo_putword(char * buf,int val)41 static char *yoohoo_putword(char *buf, int val)
42 {
43 	buf[0] = ( ((unsigned int) val)      ) & 0xff;
44 	buf[1] = ( ((unsigned int) val) >> 8 ) & 0xff;
45 
46 	return buf + 2;
47 }
48 
yoohoo_getword(const char * buf)49 static int yoohoo_getword(const char *buf)
50 {
51 	return ( (unsigned int) ((unsigned char) buf[0])      )
52 	     | ( (unsigned int) ((unsigned char) buf[1]) << 8 );
53 }
54 
55 /*****************************************************************************
56  * Make ``hello'' packet from the yoohoo sysinfo structure and put
57  * it to the buffer
58  *
59  * Arguments:
60  * 	buffer    pointer to the destination buffer (must be at least
61  * 	          128 bytes)
62  * 	hello     structure with the YooHoo system information
63  *
64  * Return value:
65  * 	None
66  */
yoohoo_put_hello(char * buffer,s_yoohoo_sysinfo * hello)67 static void yoohoo_put_hello(char *buffer, s_yoohoo_sysinfo *hello)
68 {
69 	char *p, *q;
70 
71 	ASSERT(buffer && hello);
72 
73 	memset(buffer, '\0', YOOHOO_HELLOLEN);
74 
75 	p = buffer;
76 	p = yoohoo_putword(p, 0x6f);
77 	p = yoohoo_putword(p, 0x01);                 /* Hello version */
78 	p = yoohoo_putword(p, hello->product_code);  /* Product code */
79 	p = yoohoo_putword(p, hello->version_maj);   /* Major version */
80 	p = yoohoo_putword(p, hello->version_min);   /* Minor version */
81 
82 	strnxcpy(p, hello->system, 60);              /* Node name */
83 
84 	/*
85 	 * Add domain after the end of 'Node name'
86 	 * TODO: check it for buffer overflows %-I
87 	 */
88 	if( hello->anum > 0 && hello->addrs[0].addr.domain
89 	                    && *hello->addrs[0].addr.domain )
90 	{
91 		char *q;
92 		if( strlen(hello->system) + strlen(hello->addrs[0].addr.domain) > 57 )
93 		{
94 			if( strlen(hello->addrs[0].addr.domain) < 60 )
95 				q = p + (60 - strlen(hello->addrs[0].addr.domain));
96 			else
97 				q = p;
98 
99 			*q++ = '\0';
100 		}
101 		else
102 		{
103 			q = p + strlen(hello->system) + 1;
104 		}
105 		strnxcpy(q, hello->addrs[0].addr.domain, 60-(q-p));
106 	}
107 	p += 60;
108 
109 	strnxcpy(p, hello->sysop, 20);               /* SysOp name */
110 	p += 20;
111 
112 	if( hello->anum > 0 )
113 	{
114 		p = yoohoo_putword(p, hello->addrs[0].addr.zone);
115 		p = yoohoo_putword(p, hello->addrs[0].addr.net);
116 		p = yoohoo_putword(p, hello->addrs[0].addr.node);
117 		p = yoohoo_putword(p, hello->addrs[0].addr.point);
118 	}
119 	else
120 		p += 8;
121 
122 	strncpy(p, hello->passwd, 8);                /* Session password */
123 	p += 8;
124 	p += 8;                                      /* Reserved 8 bytes */
125 	p = yoohoo_putword(p, hello->capabilities);  /* Capabilities */
126 	p += 12;                                     /* Reserved 12 bytes */
127 
128 #ifdef DEBUG
129 	DEB((D_HSHAKE, "yoohoo_put_hello: HELLO dump: \"%s\"",
130 			q = string_printable_buffer(buffer, 128)));
131 	if( q ) free(q);
132 #endif
133 
134 	ASSERT((p - buffer) == YOOHOO_HELLOLEN);
135 }
136 
137 /*****************************************************************************
138  * Extract session information from the ``hello'' packet
139  *
140  * Arguments:
141  * 	hello     pointer to the destination structure with the session
142  * 	          information
143  * 	buffer    source buffer with ``hello'' packet
144  *
145  * Return value:
146  * 	None
147  */
yoohoo_get_hello(s_yoohoo_sysinfo * hello,const char * buffer)148 static int yoohoo_get_hello(s_yoohoo_sysinfo *hello, const char *buffer)
149 {
150 	s_faddr addr;
151 
152 	ASSERT(buffer && hello);
153 
154 #ifdef DEBUG
155 	{
156 		char *q = string_printable_buffer(buffer, YOOHOO_HELLOLEN);
157 		DEB((D_HSHAKE, "yoohoo_get_hello: HELLO dump: \"%s\"", q));
158 		if( q ) free(q);
159 	}
160 #endif
161 
162 	memset(hello, '\0', sizeof(s_yoohoo_sysinfo));
163 
164 	if( yoohoo_getword(buffer+2) != 0x01 )
165 		bf_log("YooHoo hello version is %d!", yoohoo_getword(buffer+2));
166 
167 	hello->product_code  = yoohoo_getword(buffer+4);
168 	hello->version_maj   = yoohoo_getword(buffer+6);
169 	hello->version_min   = yoohoo_getword(buffer+8);
170 	strnxcpy(hello->system, buffer+10, MIN(sizeof(hello->system),60+1));
171 	strnxcpy(hello->sysop, buffer+70, MIN(sizeof(hello->sysop),20+1));
172 
173 	/*
174 	 * Extract address
175 	 */
176 	memset(&addr, '\0', sizeof(s_faddr));
177 	addr.zone  = yoohoo_getword(buffer+90);
178 	addr.net   = yoohoo_getword(buffer+92);
179 	addr.node  = yoohoo_getword(buffer+94);
180 	addr.point = yoohoo_getword(buffer+96);
181 	session_addrs_add(&hello->addrs, &hello->anum, addr);
182 
183 	strnxcpy(hello->passwd, buffer+98, MIN(sizeof(hello->passwd),8+1));
184 	/* Reserved 8 bytes */
185 	hello->capabilities = yoohoo_getword(buffer+114);
186 	/* Reserved 12 bytes */
187 
188 	return 0;
189 }
190 
yoohoo_send_hello(s_yoohoo_sysinfo * local_data)191 int yoohoo_send_hello(s_yoohoo_sysinfo *local_data)
192 {
193 	char hello_buffer[YOOHOO_HELLOLEN];
194 	unsigned short hello_crc = 0;
195 	YooHoo_SendState state = YS_Init;
196 	time_t wait_timer;
197 	int tries = 0;
198 	int rc;
199 
200 	ASSERT(local_data);
201 
202 	while(1)
203 	{
204 		switch(state) {
205 		case YS_Init:
206 			tries = 0;
207 			yoohoo_put_hello(hello_buffer, local_data);
208 			hello_crc = getcrc16xmodem(hello_buffer, YOOHOO_HELLOLEN);
209 			state = YS_SendHello;
210 			break;
211 
212 		case YS_SendHello:
213 			if( ++tries > 10 )
214 			{
215 				bf_log("too many tries sending hello");
216 				state = YS_Error;
217 				break;
218 			}
219 			else if( tries > 1 )
220 				bf_log("yoohoo hello send - retry %d", tries);
221 
222 			if( PUTCHAR(0x1f) < 0
223 			 || WRITE_TIMEOUT(hello_buffer, sizeof(hello_buffer)) < 0
224 			 || PUTCHAR(((unsigned int) hello_crc >> 8) & 0xff) < 0
225 			 || PUTCHAR(((unsigned int) hello_crc     ) & 0xff) < 0 )
226 			{
227 				state = YS_Error;
228 				break;
229 			}
230 
231 			timer_set(&wait_timer, 40);
232 			state = YS_WaitResp;
233 			break;
234 
235 		case YS_WaitResp:
236 			if( timer_expired(wait_timer) )
237 			{
238 				bf_log("time out waiting for response");
239 				state = YS_Error;
240 				break;
241 			}
242 
243 			if( (rc = GETCHAR(1)) < 0 )
244 			{
245 				if( rc != TTY_TIMEOUT )
246 				{
247 					state = YS_Error;
248 					break;
249 				}
250 			}
251 			else if( rc == XON || rc == XOFF )
252 			{
253 				/* Do nothing. Drop them down */
254 			}
255 			else if( rc == '?' || rc == ENQ )
256 			{
257 				if( rc == '?' )
258 					bf_log("remote failed to receive our HELLO packet");
259 				state = YS_SendHello;
260 			}
261 			else if( rc == ACK )
262 			{
263 				state = YS_Done;
264 			}
265 			break;
266 
267 		case YS_Done:
268 			return 0;
269 
270 		case YS_Error:
271 			return -1;
272 		}
273 	}
274 
275 	return -1; /* UNREACHABLE */
276 }
277 
yoohoo_recv_hello(s_yoohoo_sysinfo * remote_data)278 int yoohoo_recv_hello(s_yoohoo_sysinfo *remote_data)
279 {
280 	char hello_buffer[YOOHOO_HELLOLEN];
281 	int crc_local = 0;
282 	int crc_remote = 0;
283 	YooHoo_RecvState state = YR_Init;
284 	time_t wait_timer;
285 	time_t junk_timer;
286 	int hello_pos;
287 	int tries = 0;
288 	int rc;
289 
290 	ASSERT(remote_data);
291 
292 	while(1)
293 	{
294 		switch(state) {
295 		case YR_Init:
296 			tries = 0;
297 			memset(hello_buffer, '\0', sizeof(hello_buffer));
298 			state = YR_SendENQ;
299 			break;
300 
301 		case YR_SendENQ:
302 			if( ++tries > 3 )
303 			{
304 				bf_log("too many tries receiving hello");
305 				state = YR_Error;
306 				break;
307 			}
308 			else if( tries > 1 )
309 				bf_log("yoohoo hello recv - retry %d", tries);
310 
311 			if( PUTCHAR(ENQ) < 0 )
312 			{
313 				state = YR_Error;
314 				break;
315 			}
316 
317 			timer_set(&wait_timer, 120);
318 			state = YR_WaitHeader;
319 			break;
320 
321 		case YR_WaitHeader:
322 			if( timer_expired(wait_timer) )
323 			{
324 				bf_log("time out waiting for response");
325 				state = YR_Error;
326 				break;
327 			}
328 
329 			if( (rc = GETCHAR(1)) < 0 )
330 			{
331 				if( rc != TTY_TIMEOUT )
332 				{
333 					state = YR_Error;
334 					break;
335 				}
336 			}
337 			else if( rc == XON || rc == XOFF )
338 			{
339 				/* Do nothing. Drop them down */
340 			}
341 			else if( rc == 0x1f )
342 			{
343 				state = YR_RecvHello;
344 			}
345 			else
346 			{
347 				timer_set(&junk_timer, 10);
348 				state = YR_TossJunk;
349 			}
350 			break;
351 
352 		case YR_TossJunk:
353 			if( timer_expired(junk_timer) )
354 			{
355 				state = YR_SendENQ;
356 				break;
357 			}
358 
359 			if( (rc = GETCHAR(1)) < 0 )
360 			{
361 				if( rc != TTY_TIMEOUT )
362 				{
363 					state = YR_Error;
364 					break;
365 				}
366 			}
367 			else if( rc == XON || rc == XOFF )
368 			{
369 				/* Do nothing. Drop them down */
370 			}
371 			else if( rc == 0x1f )
372 			{
373 				state = YR_RecvHello;
374 			}
375 			break;
376 
377 		case YR_RecvHello:
378 			hello_pos = 0;
379 			while( (rc = GETCHAR(30)) >= 0 )
380 			{
381 				if( hello_pos < 128 )
382 				{
383 					hello_buffer[hello_pos++] = rc;
384 				}
385 				else if( hello_pos == 128 )
386 				{
387 					crc_remote = ((unsigned int) ((unsigned char) rc) << 8);
388 					++hello_pos;
389 				}
390 				else if( hello_pos == 129 )
391 				{
392 					crc_remote |= (unsigned int) ((unsigned char) rc);
393 					break;
394 				}
395 				else
396 					break;
397 			}
398 
399 			if( hello_pos != 129 )
400 			{
401 				state = YR_Error;
402 				break;
403 			}
404 
405 			/*
406 			 * Check CRC-16
407 			 */
408 			crc_local = getcrc16xmodem(hello_buffer, sizeof(hello_buffer));
409 			if( crc_local != crc_remote )
410 			{
411 				bf_log("got hello packet with incorrect checksum");
412 				state = YR_SendNAK;
413 				break;
414 			}
415 
416 			if( yoohoo_get_hello(remote_data, hello_buffer) )
417 			{
418 				bf_log("got invalid hello packet");
419 				state = YR_SendNAK;
420 			}
421 			else
422 				state = YR_SendACK;
423 			break;
424 
425 		case YR_SendNAK:
426 			if( PUTCHAR('?') < 0 )
427 				state = YR_Error;
428 			else
429 				state = YR_WaitHeader;
430 			break;
431 
432 		case YR_SendACK:
433 			if( PUTCHAR(ACK) < 0 )
434 				state = YR_Error;
435 			else
436 				state = YR_Done;
437 			break;
438 
439 		case YR_Done:
440 			return 0;
441 
442 		case YR_Error:
443 			return -1;
444 		}
445 	}
446 
447 	return -1; /* UNREACHABLE */
448 }
449 
yoohoo_set_sysinfo(s_yoohoo_sysinfo * local_data,int hrc,e_protocol protocol)450 void yoohoo_set_sysinfo(s_yoohoo_sysinfo *local_data, int hrc,
451                         e_protocol protocol)
452 {
453 	s_cval_entry *addr_ptr;
454 	s_faddr *primary = NULL;
455 
456 	const long options   = conf_options(cf_options);
457 	const char *p_system = conf_string(cf_system_name);
458 	const char *p_sysop  = conf_string(cf_sysop_name);
459 
460 	memset(local_data, '\0', sizeof(s_yoohoo_sysinfo));
461 
462 	/* Set best primary address */
463 	primary = session_get_bestaka(state.node.addr);
464 
465 	/*
466 	 * Set our local address
467 	 */
468 	if( primary )
469 		session_addrs_add(&local_data->addrs, &local_data->anum, *primary);
470 	else if( (addr_ptr = conf_first(cf_address)) )
471 		session_addrs_add(&local_data->addrs, &local_data->anum, addr_ptr->d.falist.addr);
472 
473 	if( !local_data->anum )
474 		bf_log("warning: no addresses will be presented to remote");
475 
476 	/*
477 	 * Set session password
478 	 */
479 	if( state.caller )
480 	{
481 		session_get_password(state.node.addr,
482 		                     local_data->passwd, sizeof(local_data->passwd));
483 	}
484 	else if( hrc == HRC_OK )
485 	{
486 		/* Satisfy remote with their password */
487 		const char *p = state.handshake->remote_password(state.handshake);
488 		if( p )
489 			strnxcpy(local_data->passwd, p, sizeof(local_data->passwd));
490 	}
491 
492 	if( !state.caller )
493 	{
494 		if( state.reqstat != REQS_ALLOW )
495 			local_data->capabilities |= YOOHOO_WZ_FREQ;
496 	}
497 
498 	/* compatibility codes */
499 	if( state.caller )
500 	{
501 		if( (options & OPTIONS_NO_ZMODEM) != OPTIONS_NO_ZMODEM )
502 			local_data->capabilities |= YOOHOO_ZMODEM;
503 		if( (options & OPTIONS_NO_ZEDZAP) != OPTIONS_NO_ZEDZAP )
504 			local_data->capabilities |= YOOHOO_ZEDZAP;
505 		if( (options & OPTIONS_NO_JANUS) != OPTIONS_NO_JANUS )
506 			local_data->capabilities |= YOOHOO_JANUS;
507 		if( (options & OPTIONS_NO_HYDRA) != OPTIONS_NO_HYDRA )
508 			local_data->capabilities |= YOOHOO_HYDRA;
509 	}
510 	else
511 	{
512 		switch(protocol) {
513 		case PROT_ZMODEM: local_data->capabilities |= YOOHOO_ZMODEM; break;
514 		case PROT_ZEDZAP: local_data->capabilities |= YOOHOO_ZEDZAP; break;
515 		case PROT_JANUS:  local_data->capabilities |= YOOHOO_JANUS;  break;
516 		case PROT_HYDRA:  local_data->capabilities |= YOOHOO_HYDRA;  break;
517 		default:          break;
518 		}
519 	}
520 
521 	local_data->product_code = BF_PRODCODE;
522 
523 	if( hrc == HRC_BAD_PASSWD )
524 	{
525 		local_data->version_maj = 99;
526 		local_data->version_min = 99; /* TODO */
527 	}
528 	else
529 	{
530 		local_data->version_maj = 0;
531 		local_data->version_min = 1;
532 	}
533 
534 	strnxcpy(local_data->sysop, p_sysop ? p_sysop : "Unknown", sizeof(local_data->sysop));
535 
536 	switch(hrc) {
537 	case HRC_BAD_PASSWD:
538 		strnxcpy(local_data->system, "Bad password", sizeof(local_data->system));
539 		break;
540 	case HRC_LOW_SPEED:
541 		strnxcpy(local_data->system, "Connect speed too low", sizeof(local_data->system));
542 		break;
543 	case HRC_BUSY:
544 		strnxcpy(local_data->system, "All AKAs are busy", sizeof(local_data->system));
545 		break;
546 	default:
547 		strnxcpy(local_data->system, p_system ? p_system : "Unknown", sizeof(local_data->system));
548 	}
549 }
550 
551 /*****************************************************************************
552  * Write system information to the log
553  *
554  * Arguments:
555  * 	yoohoo    structure with the system information
556  *
557  * Return value:
558  * 	None
559  */
yoohoo_log_sysinfo(s_yoohoo_sysinfo * yoohoo)560 void yoohoo_log_sysinfo(s_yoohoo_sysinfo *yoohoo)
561 {
562 	int i;
563 	char abuf[BF_MAXADDRSTR+1];
564 
565 	if( yoohoo->anum )
566 		for( i = 0; i < yoohoo->anum; i++ )
567 		{
568 			bf_log("   Address : %s", ftn_addrstr(abuf, yoohoo->addrs[i].addr));
569 		}
570 	else
571 		bf_log("   Address : <none>");
572 
573 	if( yoohoo->system[0] )
574 		bf_log("    System : %s", string_printable(yoohoo->system));
575 
576 #ifdef BFORCE_LOG_PASSWD
577 	if( yoohoo->passwd[0] )
578 		bf_log("  Password : %s", string_printable(yoohoo->passwd));
579 #endif
580 
581 	if( yoohoo->sysop[0] )
582 		bf_log("     SysOp : %s", string_printable(yoohoo->sysop));
583 
584 	if( yoohoo->product_code || yoohoo->version_maj || yoohoo->version_min )
585 	{
586 		bf_log("    Mailer : %s [%02x] %d.%d",
587 			"?", yoohoo->product_code,
588 			yoohoo->version_maj, yoohoo->version_min);
589 	}
590 }
591 
592