xref: /csrg-svn/sys/netccitt/pk_input.c (revision 43361)
1 /*
2  * Copyright (c) University of British Columbia, 1984
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Laboratory for Computation Vision and the Computer Science Department
8  * of the University of British Columbia.
9  *
10  * %sccs.include.redist.c%
11  *
12  *	@(#)pk_input.c	7.4 (Berkeley) 06/21/90
13  */
14 
15 #include "../h/param.h"
16 #include "../h/systm.h"
17 #include "../h/mbuf.h"
18 #include "../h/socket.h"
19 #include "../h/protosw.h"
20 #include "../h/socketvar.h"
21 #include "../h/errno.h"
22 
23 #include "../net/if.h"
24 
25 #include "../netccitt/x25.h"
26 #include "../netccitt/pk.h"
27 #include "../netccitt/pk_var.h"
28 
29 /*
30  *  This procedure is called by the link level whenever the link
31  *  becomes operational, is reset, or when the link goes down.
32  */
33 
34 pk_ctlinput (code, pkp)
35 register struct pkcb *pkp;
36 {
37 
38 	if (pkp == 0)
39 		return (EINVAL);
40 	switch (code) {
41 	case PRC_LINKUP:
42 		if (pkp -> pk_state == DTE_WAITING)
43 			pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
44 		break;
45 
46 	case PRC_LINKDOWN:
47 		pk_restart (pkp, -1);	/* Clear all active circuits */
48 		pkp -> pk_state = DTE_WAITING;
49 		break;
50 
51 	case PRC_LINKRESET:
52 		pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
53 		break;
54 
55 	}
56 	return (0);
57 }
58 
59 /*
60  *  X.25 PACKET INPUT
61  *
62  *  This procedure is called by a link level procedure whenever
63  *  an information frame is received. It decodes the packet and
64  *  demultiplexes based on the logical channel number.
65  *
66  */
67 
68 pk_input (m, xcp)
69 register struct mbuf *m;
70 struct x25config *xcp;
71 {
72 	register struct x25_packet *xp;
73 	register struct pklcd *lcp;
74 	register struct socket *so = 0;
75 	register struct pkcb *pkp;
76 	int  ptype, lcn, lcdstate = LISTEN;
77 	static struct x25config *lastxcp;
78 	static struct pkcb *lastpkp;
79 
80 	if (xcp == lastxcp)
81 		pkp = lastpkp;
82 	else {
83 		for (pkp = pkcbhead; ; pkp = pkp -> pk_next) {
84 			if (pkp == 0) {
85 				pk_message (0, xcp, "pk_input: unknown network");
86 				m_freem (m);
87 				return;
88 			}
89 			if (pkp -> pk_xcp == xcp)
90 				break;
91 		}
92 		lastxcp = xcp;
93 		lastpkp = pkp;
94 	}
95 
96 	xp = mtod (m, struct x25_packet *);
97 	ptype = pk_decode (xp);
98 	lcn = xp -> logical_channel_number;
99 	lcp = pkp -> pk_chan[lcn];
100 
101 	/*
102 	 *  If the DTE is in Restart  state, then it will ignore data,
103 	 *  interrupt, call setup and clearing, flow control and reset
104 	 *  packets.
105 	 */
106 	if (lcn < 0 || lcn > pkp -> pk_maxlcn) {
107 		pk_message (lcn, pkp -> pk_xcp, "illegal lcn");
108 		m_freem (m);
109 		return;
110 	}
111 
112 	pk_trace (pkp -> pk_xcp, xp, "P-In");
113 
114 	if (pkp -> pk_state != DTE_READY && ptype != RESTART && ptype != RESTART_CONF) {
115 		m_freem (m);
116 		return;
117 	}
118 	if (lcp) {
119 		so = lcp -> lcd_so;
120 		lcdstate = lcp -> lcd_state;
121 	} else {
122 		if (ptype == CLEAR) {	/* idle line probe (Datapac specific) */
123 			/* send response on lcd 0's output queue */
124 			lcp -> lcd_template = pk_template (lcn, X25_CLEAR_CONFIRM);
125 			pk_output (lcp);
126 			m_freem (m);
127 			return;
128 		}
129 		if (ptype != CALL)
130 			ptype = INVALID_PACKET;
131 	}
132 
133 	if (lcn == 0 && ptype != RESTART && ptype != RESTART_CONF) {
134 		pk_message (0, pkp -> pk_xcp, "illegal ptype (%s) on lcn 0",
135 			pk_name[ptype / MAXSTATES]);
136 		m_freem (m);
137 		return;
138 	}
139 
140 	switch (ptype + lcdstate) {
141 	/*
142 	 *  Incoming Call packet received.
143 	 */
144 	case CALL + LISTEN:
145 		incoming_call (pkp, xp, m -> m_len);
146 		break;
147 
148 	/*
149 	 *  Call collision: Just throw this "incoming call" away since
150 	 *  the DCE will ignore it anyway.
151 	 */
152 	case CALL + SENT_CALL:
153 		pk_message ((int)xp -> logical_channel_number, pkp -> pk_xcp,
154 			"incoming call collision");
155 		break;
156 
157 	/*
158 	 *  Call confirmation packet received. This usually means our
159 	 *  previous connect request is now complete.
160 	 */
161 	case CALL_ACCEPTED + SENT_CALL:
162 		call_accepted (lcp, xp, m -> m_len);
163 		break;
164 
165 	/*
166 	 *  This condition can only happen if the previous state was
167 	 *  SENT_CALL. Just ignore the packet, eventually a clear
168 	 *  confirmation should arrive.
169 	 */
170 	case CALL_ACCEPTED + SENT_CLEAR:
171 		break;
172 
173 	/*
174 	 *  Clear packet received. This requires a complete tear down
175 	 *  of the virtual circuit.  Free buffers and control blocks.
176 	 *  and send a clear confirmation.
177 	 */
178 	case CLEAR + READY:
179 	case CLEAR + RECEIVED_CALL:
180 	case CLEAR + SENT_CALL:
181 	case CLEAR + DATA_TRANSFER:
182 		lcp -> lcd_state = RECEIVED_CLEAR;
183 		lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR_CONFIRM);
184 		pk_output (lcp);
185 		pk_clearcause (pkp, xp);
186 		pk_close (lcp);
187 		break;
188 
189 	/*
190 	 *  Clear collision: Treat this clear packet as a confirmation.
191 	 */
192 	case CLEAR + SENT_CLEAR:
193 		pk_close (lcp);
194 		break;
195 
196 	/*
197 	 *  Clear confirmation received. This usually means the virtual
198 	 *  circuit is now completely removed.
199 	 */
200 	case CLEAR_CONF + SENT_CLEAR:
201 		pk_close (lcp);
202 		break;
203 
204 	/*
205 	 *  A clear confirmation on an unassigned logical channel - just
206 	 *  ignore it. Note: All other packets on an unassigned channel
207 	 *  results in a clear.
208 	 */
209 	case CLEAR_CONF + READY:
210 		break;
211 
212 	/*
213 	 *  Data packet received. Pass on to next level. Move the Q and M
214 	 *  bits into the data portion for the next level.
215 	 */
216 	case DATA + DATA_TRANSFER:
217 		if (lcp -> lcd_reset_condition) {
218 			ptype = DELETE_PACKET;
219 			break;
220 		}
221 
222 		/*
223 		 *  Process the P(S) flow control information in this Data packet.
224 		 *  Check that the packets arrive in the correct sequence and that
225 		 *  they are within the "lcd_input_window". Input window rotation is
226 		 *  initiated by the receive interface.
227 		 */
228 
229 		if (PS(xp) != ((lcp -> lcd_rsn + 1) % MODULUS) ||
230 			PS(xp) == ((lcp -> lcd_input_window + lcp->lcd_windowsize) % MODULUS)) {
231 			m_freem (m);
232 			pk_procerror (RESET, lcp, "p(s) flow control error");
233 			break;
234 		}
235 		lcp -> lcd_rsn = PS(xp);
236 
237 		if (pk_ack (lcp, PR(xp)) != PACKET_OK) {
238 			m_freem (m);
239 			break;
240 		}
241 
242 		m -> m_data += PKHEADERLN;
243 		m -> m_len -= PKHEADERLN;
244 		if (lcp -> lcd_flags & X25_MQBIT) {
245 			octet *t;
246 
247 			m -> m_data -= 1;
248 			m -> m_len += 1;
249 			t = mtod (m, octet *);
250 			*t = 0x00;
251 			if (xp -> q_bit)
252 				*t |= 0x80;
253 			if (MBIT(xp))
254 				*t |= 0x40;
255 		}
256 
257 		/*
258 		 * Discard Q-BIT packets if the application
259 		 * doesn't want to be informed of M and Q bit status
260 		 */
261 		if (xp -> q_bit && (lcp -> lcd_flags & X25_MQBIT) == 0) {
262 			m_freem (m);
263 			lcp -> lcd_rxcnt++;
264 			/*
265 			 * NB.  This is dangerous: sending a RR here can
266 			 * cause sequence number errors if a previous data
267 			 * packet has not yet been passed up to the application
268 			 * (RR's are normally generated via PRU_RCVD).
269 			 */
270 			lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RR);
271 			pk_output (lcp);
272 		} else {
273 #ifdef BSD4_3
274 			sbappendrecord (&so -> so_rcv, m);
275 #else
276 			sbappend (&so -> so_rcv, m);
277 #endif
278 			sorwakeup (so);
279 		}
280 		break;
281 
282 	/*
283 	 *  Interrupt packet received.
284 	 */
285 	case INTERRUPT + DATA_TRANSFER:
286 		if (lcp -> lcd_reset_condition)
287 			break;
288 		lcp -> lcd_intrdata = xp -> packet_data;
289 		sohasoutofband (so);
290 		lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT_CONFIRM);
291 		pk_output (lcp);
292 		break;
293 
294 	/*
295 	 *  Interrupt confirmation packet received.
296 	 */
297 	case INTERRUPT_CONF + DATA_TRANSFER:
298 		if (lcp -> lcd_reset_condition)
299 			break;
300 		if (lcp -> lcd_intrconf_pending == TRUE)
301 			lcp -> lcd_intrconf_pending = FALSE;
302 		else
303 			pk_procerror (RESET, lcp, "unexpected packet");
304 		break;
305 
306 	/*
307 	 *  Receiver ready received. Rotate the output window and output
308 	 *  any data packets waiting transmission.
309 	 */
310 	case RR + DATA_TRANSFER:
311 		if (lcp -> lcd_reset_condition)
312 			break;
313 		if (pk_ack (lcp, PR(xp)) != PACKET_OK)
314 			break;
315 		if (lcp -> lcd_rnr_condition == TRUE)
316 			lcp -> lcd_rnr_condition = FALSE;
317 		pk_output (lcp);
318 		break;
319 
320 	/*
321 	 *  Receiver Not Ready received. Packets up to the P(R) can be
322 	 *  be sent. Condition is cleared with a RR.
323 	 */
324 	case RNR + DATA_TRANSFER:
325 		if (lcp -> lcd_reset_condition)
326 			break;
327 		if (pk_ack (lcp, PR(xp)) != PACKET_OK)
328 			break;
329 		lcp -> lcd_rnr_condition = TRUE;
330 		break;
331 
332 	/*
333 	 *  Reset packet received. Set state to FLOW_OPEN.  The Input and
334 	 *  Output window edges ar set to zero. Both the send and receive
335 	 *  numbers are reset. A confirmation is returned.
336 	 */
337 	case RESET + DATA_TRANSFER:
338 		if (lcp -> lcd_reset_condition)
339 			/* Reset collision. Just ignore packet. */
340 			break;
341 
342 		pk_resetcause (pkp, xp);
343 		sbflush (&so -> so_snd);
344 		sbflush (&so -> so_rcv);
345 
346 		wakeup ((caddr_t) & so -> so_timeo);
347 		sorwakeup (so);
348 		sowwakeup (so);
349 
350 		lcp -> lcd_window_condition = lcp -> lcd_rnr_condition =
351 			lcp -> lcd_intrconf_pending = FALSE;
352 		lcp -> lcd_output_window = lcp -> lcd_input_window =
353 			lcp -> lcd_last_transmitted_pr = 0;
354 		lcp -> lcd_ssn = 0;
355 		lcp -> lcd_rsn = MODULUS - 1;
356 
357 		lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET_CONFIRM);
358 		pk_output (lcp);
359 		break;
360 
361 	/*
362 	 *  Reset confirmation received.
363 	 */
364 	case RESET_CONF + DATA_TRANSFER:
365 		if (lcp -> lcd_reset_condition) {
366 			lcp -> lcd_reset_condition = FALSE;
367 			pk_output (lcp);
368 		}
369 		else
370 			pk_procerror (RESET, lcp, "unexpected packet");
371 		break;
372 
373 	case DATA + SENT_CLEAR:
374 		ptype = DELETE_PACKET;
375 	case RR + SENT_CLEAR:
376 	case RNR + SENT_CLEAR:
377 	case INTERRUPT + SENT_CLEAR:
378 	case INTERRUPT_CONF + SENT_CLEAR:
379 	case RESET + SENT_CLEAR:
380 	case RESET_CONF + SENT_CLEAR:
381 		/* Just ignore packet if we have sent a CLEAR already.
382 		   */
383 		break;
384 
385 	/*
386 	 *  Restart sets all the permanent virtual circuits to the "Data
387 	 *  Transfer" stae and  all the switched virtual circuits to the
388 	 *  "Ready" state.
389 	 */
390 	case RESTART + READY:
391 		switch (pkp -> pk_state) {
392 		case DTE_SENT_RESTART:
393 			/* Restart collision. */
394 			pkp -> pk_state = DTE_READY;
395 			pk_message (0, pkp -> pk_xcp,
396 				"Packet level operational");
397 			break;
398 
399 		default:
400 			pk_restart (pkp, -1);
401 			pk_restartcause (pkp, xp);
402 			pkp -> pk_chan[0] -> lcd_template = pk_template (0,
403 				X25_RESTART_CONFIRM);
404 			pk_output (pkp -> pk_chan[0]);
405 		}
406 		break;
407 
408 	/*
409 	 *  Restart confirmation received. All logical channels are set
410 	 *  to READY.
411 	 */
412 	case RESTART_CONF + READY:
413 		switch (pkp -> pk_state) {
414 		case DTE_SENT_RESTART:
415 			pkp -> pk_state = DTE_READY;
416 			pk_message (0, pkp -> pk_xcp,
417 				"Packet level operational");
418 			break;
419 
420 		default:
421 			/* Restart local procedure error. */
422 			pk_restart (pkp, X25_RESTART_LOCAL_PROCEDURE_ERROR);
423 			pkp -> pk_state = DTE_SENT_RESTART;
424 		}
425 		break;
426 
427 	default:
428 		if (lcp) {
429 			pk_procerror (CLEAR, lcp, "unknown packet error");
430 			pk_message (lcn, pkp -> pk_xcp,
431 				"\"%s\" unexpected in \"%s\" state",
432 				pk_name[ptype/MAXSTATES], pk_state[lcdstate]);
433 		}
434 		else	/* Packets arrived on an unassigned channel.
435 			*/
436 			pk_message ((int)xp->logical_channel_number, pkp -> pk_xcp,
437 				"packet arrived on unassigned lcn");
438 		break;
439 	}
440 	if (ptype != DATA)
441 		m_freem (m);
442 }
443 
444 
445 /*
446  * This routine handles incoming call packets. It matches the protocol
447  * field on the Call User Data field (usually the first four bytes) with
448  * sockets awaiting connections.
449  */
450 
451 static
452 incoming_call (pkp, xp, len)
453 struct pkcb *pkp;
454 struct x25_packet *xp;
455 {
456 	register struct pklcd *lcp = 0, *l;
457 	register struct sockaddr_x25 *sa;
458 	register struct x25_calladdr *a;
459 	register struct socket *so = 0;
460 	struct mbuf *m;
461 	register int l1, l2;
462 	char *e, *errstr = "server unavailable";
463 	octet *u;
464 	int lcn = xp -> logical_channel_number;
465 
466 	/* First, copy the data from the incoming call packet to a X25_socket
467 	   descriptor. */
468 
469 	a = (struct x25_calladdr *) &xp -> packet_data;
470 	l1 = a -> calling_addrlen;
471 	l2 = a -> called_addrlen;
472 	if ((m = m_getclr (M_DONTWAIT, MT_HEADER)) == 0)
473 		return;
474 	sa = mtod (m, struct sockaddr_x25 *);
475 	u = (octet *) (a -> address_field + l2 / 2);
476 	e = sa -> x25_addr;
477 	if (l2 & 0x01) {
478 		*e++ = *u++ & 0x0f;
479 		l1--;
480 	}
481 	from_bcd (e, &u, l1);
482 	if (l1 & 0x01)
483 		u++;
484 
485 	parse_facilities (u, sa);
486 	u += *u + 1;
487 	sa -> x25_udlen = min (16, ((octet *)xp) + len - u);
488 	if (sa -> x25_udlen < 0)
489 		sa -> x25_udlen = 0;
490 	bcopy ((caddr_t)u, sa -> x25_udata, (unsigned)sa -> x25_udlen);
491 
492 	/*
493 	 * Now, loop through the  listen sockets looking for a match on the
494 	 * PID. That is  the first  four octets  of the user data field.  This
495 	 * is the closest thing to a port number for X.25 packets. What it
496 	 * does provide is away of  multiplexing  services at the user level.
497 	 */
498 
499 	for (l = pk_listenhead; l; l = l -> lcd_listen) {
500 		struct sockaddr_x25 *sxp = l -> lcd_ceaddr;
501 
502 		if (bcmp (sxp -> x25_udata, sa -> x25_udata, sxp->x25_udlen))
503 			continue;
504 		if (sxp -> x25_net && sxp -> x25_net != pkp->pk_xcp->xc_net)
505 			continue;
506 		/*
507 		 * don't accept incoming collect calls unless
508 		 * the server sets the reverse charging option.
509 		 */
510 		if ((sxp -> x25_opts.op_flags & (X25_OLDSOCKADDR|X25_REVERSE_CHARGE)) == 0 &&
511 			sa -> x25_opts.op_flags & X25_REVERSE_CHARGE) {
512 			errstr = "incoming collect call refused";
513 			break;
514 		}
515 		if (l -> lcd_so) {
516 			if (so = sonewconn (l -> lcd_so, SO_ISCONNETED))
517 				    lcp = (struct pklcd *) so -> so_pcb;
518 		} else
519 			lcp = pk_attach((struct socket *) 0);
520 		if (lcp == 0) {
521 			/*
522 			 * Insufficient space or too many unaccepted
523 			 * connections.  Just throw the call away.
524 			 */
525 			errstr = "server malfunction";
526 			break;
527 		}
528 		lcp -> lcd_upper = l -> lcd_upper;
529 		lcp -> lcd_upnext = l -> lcd_upnext;
530 		lcp -> lcd_lcn = lcn;
531 		lcp -> lcd_state = RECEIVED_CALL;
532 		lcp -> lcd_craddr = sa;
533 		sa -> x25_opts.op_flags |= sxp -> x25_opts.op_flags &
534 			~X25_REVERSE_CHARGE;
535 		pk_assoc (pkp, lcp, sa);
536 		lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL_ACCEPTED);
537 		if (so) {
538 			pk_output (lcp);
539 			soisconnected (so);
540 		} else if (lcp->lcd_upper)
541 			(*lcp->lcd_upper)(lcp);
542 		return;
543 	}
544 
545 	/*
546 	 * If the call fails for whatever reason, we still need to build a
547 	 * skeleton LCD in order to be able to properly  receive the CLEAR
548 	 * CONFIRMATION.
549 	 */
550 #ifdef WATERLOO		/* be explicit */
551 	if (l == 0 && bcmp(sa->x25_udata, "ean", 3) == 0)
552 		pk_message (lcn, pkp -> pk_xcp, "host=%s ean%c: %s",
553 			sa->x25_addr, sa->x25_udata[3] & 0xff, errstr);
554 	else if (l == 0 && bcmp(sa->x25_udata, "\1\0\0\0", 4) == 0)
555 		pk_message (lcn, pkp -> pk_xcp, "host=%s x29d: %s",
556 			sa->x25_addr, errstr);
557 	else
558 #endif
559 	pk_message (lcn, pkp -> pk_xcp, "host=%s pid=%x %x %x %x: %s",
560 		sa -> x25_addr, sa -> x25_udata[0] & 0xff,
561 		sa -> x25_udata[1] & 0xff, sa -> x25_udata[2] & 0xff,
562 		sa -> x25_udata[3] & 0xff, errstr);
563 	if ((m = m_getclr (M_DONTWAIT, MT_HEADER)) == 0) {
564 		(void) m_free (dtom (sa));
565 		return;
566 	}
567 	lcp = mtod (m, struct pklcd *);
568 	lcp -> lcd_lcn = lcn;
569 	lcp -> lcd_state = RECEIVED_CALL;
570 	pk_assoc (pkp, lcp, sa);
571 	(void) m_free (dtom (sa));
572 	pk_clear (lcp);
573 }
574 
575 static
576 call_accepted (lcp, xp, len)
577 struct pklcd *lcp;
578 struct x25_packet *xp;
579 {
580 	register struct x25_calladdr *ap;
581 	register octet *fcp;
582 
583 	lcp -> lcd_state = DATA_TRANSFER;
584 	soisconnected (lcp -> lcd_so);
585 	if (len > 3) {
586 		ap = (struct x25_calladdr *) &xp -> packet_data;
587 		fcp = (octet *) ap -> address_field + (ap -> calling_addrlen +
588 			ap -> called_addrlen + 1) / 2;
589 		if (fcp + *fcp <= ((octet *)xp) + len)
590 			parse_facilities (fcp, lcp -> lcd_ceaddr);
591 	}
592 	pk_assoc (lcp -> lcd_pkp, lcp, lcp -> lcd_ceaddr);
593 }
594 
595 static
596 parse_facilities (fcp, sa)
597 register octet *fcp;
598 register struct sockaddr_x25 *sa;
599 {
600 	register octet *maxfcp;
601 
602 	maxfcp = fcp + *fcp;
603 	fcp++;
604 	while (fcp < maxfcp) {
605 		/*
606 		 * Ignore national DCE or DTE facilities
607 		 */
608 		if (*fcp == 0 || *fcp == 0xff)
609 			break;
610 		switch (*fcp) {
611 		case FACILITIES_WINDOWSIZE:
612 			sa -> x25_opts.op_wsize = fcp[1];
613 			fcp += 3;
614 			break;
615 
616 		case FACILITIES_PACKETSIZE:
617 			sa -> x25_opts.op_psize = fcp[1];
618 			fcp += 3;
619 			break;
620 
621 		case FACILITIES_THROUGHPUT:
622 			sa -> x25_opts.op_speed = fcp[1];
623 			fcp += 2;
624 			break;
625 
626 		case FACILITIES_REVERSE_CHARGE:
627 			if (fcp[1] & 01)
628 				sa -> x25_opts.op_flags |= X25_REVERSE_CHARGE;
629 			/*
630 			 * Datapac specific: for a X.25(1976) DTE, bit 2
631 			 * indicates a "hi priority" (eg. international) call.
632 			 */
633 			if (fcp[1] & 02 && sa -> x25_opts.op_psize == 0)
634 				sa -> x25_opts.op_psize = X25_PS128;
635 			fcp += 2;
636 			break;
637 
638 		default:
639 /*printf("unknown facility %x, class=%d\n", *fcp, (*fcp & 0xc0) >> 6);*/
640 			switch ((*fcp & 0xc0) >> 6) {
641 			case 0:			/* class A */
642 				fcp += 2;
643 				break;
644 
645 			case 1:
646 				fcp += 3;
647 				break;
648 
649 			case 2:
650 				fcp += 4;
651 				break;
652 
653 			case 3:
654 				fcp++;
655 				fcp += *fcp;
656 			}
657 		}
658 	}
659 }
660 
661 from_bcd (a, x, len)
662 register char *a;
663 register octet **x;
664 register int len;
665 {
666 	register int posn = 0;
667 
668 	while (--len >= 0) {
669 		if (posn++ & 0x01)
670 			*a = *(*x)++ & 0x0f;
671 		else
672 			*a = (**x >> 4) & 0x0F;
673 		*a++ |= 0x30;
674 	}
675 }
676