format.c 11.9 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* format.c
 *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
J.H.M. Dassen's avatar
J.H.M. Dassen committed
23
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
24
25
 */

Niels Möller's avatar
Niels Möller committed
26
#include "format.h"
27
28

#include "list.h"
Niels Möller's avatar
Niels Möller committed
29
30
31
#include "werror.h"
#include "xalloc.h"

32
33
34
#include <assert.h>
#include <string.h>

35
struct lsh_string *ssh_format(const char *format, ...)
Niels Möller's avatar
Niels Möller committed
36
37
38
{
  va_list args;
  UINT32 length;
Niels Möller's avatar
Niels Möller committed
39
  struct lsh_string *packet;
Niels Möller's avatar
Niels Möller committed
40
41
42
43
44

  va_start(args, format);
  length = ssh_vformat_length(format, args);
  va_end(args);

Niels Möller's avatar
Niels Möller committed
45
  packet = lsh_string_alloc(length);
Niels Möller's avatar
Niels Möller committed
46
47

  va_start(args, format);
48
  ssh_vformat_write(format, length, packet->data, args);
Niels Möller's avatar
Niels Möller committed
49
50
51
52
  va_end(args);

  return packet;
}
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
struct lsh_string *ssh_cformat(const char *format, ...)
{
  va_list args;
  UINT32 length;
  struct lsh_string *packet;

  va_start(args, format);
  length = ssh_vformat_length(format, args);
  va_end(args);

  packet = lsh_string_alloc(length + 1);

  va_start(args, format);
  ssh_vformat_write(format, length, packet->data, args);
  va_end(args);

  if (memchr(packet->data, '\0', length))
    {
      lsh_string_free(packet);
      return NULL;
    }
  
  packet->data[length] = '\0';
  packet->length--;

  assert(packet->length == length);
  return packet;
}

83
UINT32 ssh_format_length(const char *format, ...)
84
85
86
87
88
89
90
91
92
93
94
{
  va_list args;
  UINT32 length;

  va_start(args, format);
  length = ssh_vformat_length(format, args);
  va_end(args);

  return length;
}

95
void ssh_format_write(const char *format, UINT32 length, UINT8 *buffer, ...)
96
97
98
99
{
  va_list args;
  
  va_start(args, buffer);
Niels Möller's avatar
Niels Möller committed
100
  ssh_vformat_write(format, length, buffer, args);
101
102
103
  va_end(args);
}
     
104
static int write_decimal_length(UINT8 *buffer, UINT32 n);
105
static void format_hex_string(UINT8 *buffer, UINT32 length, const UINT8 *data);
106

107
UINT32 ssh_vformat_length(const char *f, va_list args)
Niels Möller's avatar
Niels Möller committed
108
109
110
111
112
113
114
115
{
  UINT32 length = 0;

  while(*f)
    {
      if (*f == '%')
	{
	  int literal = 0;
116
	  int decimal = 0;
117
	  int unsigned_form = 0;
118
	  int hex = 0;
119
120
	  
	  while(*++f)
Niels Möller's avatar
Niels Möller committed
121
	    {
122
	      switch (*f)
123
		{
124
		case 'l':
125
		  literal = 1;
126
127
128
129
130
131
132
		  break;
		case 'd':
		  decimal = 1;
		  break;
		case 'f':
		  /* Do nothing */
		  break;
133
134
135
		case 'u':
		  unsigned_form = 1;
		  break;
136
137
138
		case 'x':
		  hex = 1;
		  break;
139
140
		default:
		  goto end_options;
141
		}
Niels Möller's avatar
Niels Möller committed
142
	    }
143
144
145
146
end_options:

	  if (literal && decimal)
	    fatal("Internal error!\n");
147
	  
148
	  switch(*f++)
Niels Möller's avatar
Niels Möller committed
149
150
	    {
	    case 'c':
151
	      (void) va_arg(args, int);
Niels Möller's avatar
Niels Möller committed
152
153
154
155
156
157
	      /* Fall through */
	    case '%':
	      length++;
	      break;

	    case 'i':
158
159
160
161
162
163
164
165
166
	      {
		UINT32 i = va_arg(args, UINT32);
		if (decimal)
		  length += format_size_in_decimal(i);
		else
		  length += 4;
		break;
	      }

Niels Möller's avatar
Niels Möller committed
167
168
	    case 's':
	      {
169
		UINT32 l = va_arg(args, UINT32); /* String length */ 
Niels Möller's avatar
Niels Möller committed
170
		(void) va_arg(args, const UINT8 *);    /* data */
Niels Möller's avatar
Niels Möller committed
171

172
173
		length += l;

174
175
176
		if (hex)
		  length += l;

177
		if (decimal)
178
		  length += format_size_in_decimal(l) + 1;
179
		else if (!literal)
Niels Möller's avatar
Niels Möller committed
180
		  length += 4;
181
182

		break;
Niels Möller's avatar
Niels Möller committed
183
	      }
Niels Möller's avatar
Niels Möller committed
184
	    case 'S':
185
186
187
	      {
		struct lsh_string *s = va_arg(args, struct lsh_string *);
		length += s->length;
188
189
190

		if (hex)
		  length += s->length;
191
192
		
		if (decimal)
193
		  length += format_size_in_decimal(s->length) + 1;
194
195
196
197
198
		else if (!literal)
		  length += 4;
		
		break;
	      }
Niels Möller's avatar
Niels Möller committed
199
	    case 'z':
200
	      {
Niels Möller's avatar
Niels Möller committed
201
		unsigned l = strlen(va_arg(args, const char *));
202
203
		length += l;

204
		if (decimal)
205
		  length += format_size_in_decimal(l) + 1;
206
207
208
209
210
		
		else if (!literal)
		  length += 4;
		break;
	      }
Niels Möller's avatar
Niels Möller committed
211
	    case 'r':
212
213
	      {
		UINT32 l = va_arg(args, UINT32); 
214
215
		length += l;
		(void) va_arg(args, UINT8 **);    /* pointer */
Niels Möller's avatar
Niels Möller committed
216

217
		if (decimal)
218
		  length += format_size_in_decimal(l) + 1;
219
220
221
222
223
		else if (!literal)
		  length += 4;

		break;
	      }
Niels Möller's avatar
Niels Möller committed
224
225
226
	    case 'a':
	      {
		int atom = va_arg(args, int);
227
228
		int l;
		
Niels Möller's avatar
Niels Möller committed
229
230
		assert(atom);

231
		l = get_atom_length(atom);
232
		length += l;
Niels Möller's avatar
Niels Möller committed
233

234
		if (decimal)
235
		  length += format_size_in_decimal(l) + 1;
236
		else if (!literal)
Niels Möller's avatar
Niels Möller committed
237
		  length += 4;
238

239
		break;
Niels Möller's avatar
Niels Möller committed
240
	      }
241
242
	    case 'A':
	      {
243
		struct int_list *l = va_arg(args, struct int_list *);
244
		UINT32 n, i;
245

246
		if (decimal)
247
		  fatal("ssh_format: Decimal lengths not supported for %%A\n");
248
		
249
		for(n = i =0; i < LIST_LENGTH(l); i++)
250
		  {
251
		    if (LIST(l)[i])
252
253
		      {
			n++;
254
			length += get_atom_length(LIST(l)[i]);
255
		      }
256
		  }
257
258
259
260
		if (n)
		  /* One ','-character less than the number of atoms */
		  length += (n-1);
		    
261
262
		if (!literal)
		  length += 4;
263

264
265
		break;
	      }
Niels Möller's avatar
Niels Möller committed
266
267
	    case 'n':
	      {
268
		MP_INT *n = va_arg(args, MP_INT*);
Niels Möller's avatar
Niels Möller committed
269
270

		/* Calculate length of written number */
271
272
273
274
275
276
277
278
		unsigned l;
		if (unsigned_form)
		  {
		    assert(mpz_sgn(n) >= 0);
		    l = bignum_format_u_length(n);
		  }
		else
		  l = bignum_format_s_length(n);
279
280

		length += l;
Niels Möller's avatar
Niels Möller committed
281

282
283
284
		/* Decimal not supported. */
		assert(!decimal);
#if 0
285
		if (decimal)
286
		  {
287
		    length += format_size_in_decimal(l) + 1;
288
		  }
289
290
291
		else
#endif
		if (!literal)
Niels Möller's avatar
Niels Möller committed
292
		  length += 4;
293

294
		break;
Niels Möller's avatar
Niels Möller committed
295
	      }
296
297
298
	    default:
	      fatal("ssh_vformat_length: bad format string");
	      break;
Niels Möller's avatar
Niels Möller committed
299
300
301
302
303
304
305
306
307
308
309
	    }
	}
      else
	{
	  length++;
	  f++;
	}
    }
  return length;
}

310
void ssh_vformat_write(const char *f, UINT32 size, UINT8 *buffer, va_list args)
Niels Möller's avatar
Niels Möller committed
311
{
312
313
  UINT8 *start = buffer;
  
Niels Möller's avatar
Niels Möller committed
314
  while(*f)
Niels Möller's avatar
Niels Möller committed
315
316
317
318
    {
      if (*f == '%')
	{
	  int literal = 0;
319
	  int do_free = 0;
320
	  int decimal = 0;
321
	  int hex = 0;
322
	  int unsigned_form = 0;
323
324
	  
	  while(*++f)
Niels Möller's avatar
Niels Möller committed
325
	    {
326
	      switch (*f)
327
		{
328
		case 'l':
329
		  literal = 1;
330
331
332
333
334
		  break;
		case 'd':
		  decimal = 1;
		  break;
		case 'f':
335
		  do_free = 1;
336
		  break;
337
338
339
		case 'u':
		  unsigned_form = 1;
		  break;
340
341
342
		case 'x':
		  hex = 1;
		  break;
343
344
		default:
		  goto end_options;
345
		}
346
	    }
Niels Möller's avatar
Niels Möller committed
347
	end_options:
348
349
350
351
		  
	  if (literal && decimal)
	    fatal("Internal error!\n");

352
	  switch(*f++)
Niels Möller's avatar
Niels Möller committed
353
354
	    {
	    case 'c':
355
356
	      *buffer++ = va_arg(args, int);

Niels Möller's avatar
Niels Möller committed
357
358
359
	      break;
	    case '%':
	      *buffer++ = '%';
360

Niels Möller's avatar
Niels Möller committed
361
362
363
364
365
	      break;

	    case 'i':
	      {
		UINT32 i = va_arg(args, UINT32);
366
367
368
369
370
371
372
373
374
375
376
		if (decimal)
		  {
		    unsigned length = format_size_in_decimal(i);
		    format_decimal(length, buffer, i);
		    buffer += length;
		  }
		else
		  {
		    WRITE_UINT32(buffer, i);
		    buffer += 4;
		  }
377
		break;
Niels Möller's avatar
Niels Möller committed
378
	      }
379

Niels Möller's avatar
Niels Möller committed
380
381
	    case 's':
	      {
382
		UINT32 size = va_arg(args, UINT32);
Niels Möller's avatar
Niels Möller committed
383
		const UINT8 *data = va_arg(args, const UINT8 *);
Niels Möller's avatar
Niels Möller committed
384

385
386
		UINT32 length = hex ? (2*size) : size;

387
388
389
		if (decimal)
		  buffer += write_decimal_length(buffer, length);
		else if (!literal)
Niels Möller's avatar
Niels Möller committed
390
391
392
393
394
		  {
		    WRITE_UINT32(buffer, length);
		    buffer += 4;
		  }

395
396
397
398
399
		if (hex)
		  format_hex_string(buffer, size, data);
		else
		  memcpy(buffer, data, size);
		
Niels Möller's avatar
Niels Möller committed
400
		buffer += length;
401
402

		break;
Niels Möller's avatar
Niels Möller committed
403
	      }
Niels Möller's avatar
Niels Möller committed
404
405
406
	    case 'S':
	      {
		struct lsh_string *s = va_arg(args, struct lsh_string *);
407
408
409
410
		UINT32 length = s->length;

		if (hex)
		  length *= 2;
Niels Möller's avatar
Niels Möller committed
411

412
		if (decimal)
413
		  buffer += write_decimal_length(buffer, length);
414
415

		else if (!literal)
Niels Möller's avatar
Niels Möller committed
416
		  {
417
		    WRITE_UINT32(buffer, length);
Niels Möller's avatar
Niels Möller committed
418
419
420
		    buffer += 4;
		  }

421
422
423
424
425
426
		if (hex)
		  format_hex_string(buffer, s->length, s->data);
		else
		  memcpy(buffer, s->data, s->length);

		buffer += length;
427
428
429

		if (do_free)
		  lsh_string_free(s);
430

Niels Möller's avatar
Niels Möller committed
431
		break;
Niels Möller's avatar
Niels Möller committed
432
	      }
Niels Möller's avatar
Niels Möller committed
433
434
	    case 'z':
	      {
Niels Möller's avatar
Niels Möller committed
435
		const char *s = va_arg(args, const char *);
Niels Möller's avatar
Niels Möller committed
436
		UINT32 length = strlen(s);
437
438
439
440

		if (decimal)
		  buffer += write_decimal_length(buffer, length);

Niels Möller's avatar
Niels Möller committed
441
442
443
444
445
446
447
448
		if (!literal)
		  {
		    WRITE_UINT32(buffer, length);
		    buffer += 4;
		  }

		memcpy(buffer, s, length);
		buffer += length;
449

Niels Möller's avatar
Niels Möller committed
450
451
		break;
	      }
Niels Möller's avatar
Niels Möller committed
452
453
454
455
456
	    case 'r':
	      {
		UINT32 length = va_arg(args, UINT32);
		UINT8 **p = va_arg(args, UINT8 **);

457
458
459
		if (decimal)
		  buffer += write_decimal_length(buffer, length);
		else if (!literal)
Niels Möller's avatar
Niels Möller committed
460
461
462
463
464
465
466
467
		  {
		    WRITE_UINT32(buffer, length);
		    buffer += 4;
		  }

		if (p)
		  *p = buffer;
		buffer += length;
468
469

		break;
Niels Möller's avatar
Niels Möller committed
470
471
	      }
	    
Niels Möller's avatar
Niels Möller committed
472
473
	    case 'a':
	      {
Niels Möller's avatar
Niels Möller committed
474
		UINT32 length;
Niels Möller's avatar
Niels Möller committed
475
		int atom = va_arg(args, int);
Niels Möller's avatar
Niels Möller committed
476
		
Niels Möller's avatar
Niels Möller committed
477
478
		assert(atom);

Niels Möller's avatar
Niels Möller committed
479
		length = get_atom_length(atom);
Niels Möller's avatar
Niels Möller committed
480

481
482
483
		if (decimal)
		  buffer += write_decimal_length(buffer, length);
		else if (!literal)
Niels Möller's avatar
Niels Möller committed
484
485
486
487
488
489
490
		  {
		    WRITE_UINT32(buffer, length);
		    buffer += 4;
		  }

		memcpy(buffer, get_atom_name(atom), length);
		buffer += length;
491

492
		break;
Niels Möller's avatar
Niels Möller committed
493
	      }
494
495
	    case 'A':
	      {
496
		struct int_list *l = va_arg(args, struct int_list *);
497
		UINT8 *start = buffer; /* Where to store the length */
498
		UINT32 n, i;
499
		
500
		if (decimal)
501
		  fatal("ssh_format: Decimal lengths not supported for %%A\n");
502

503
504
		if (!literal)
		  buffer += 4;
505
		
506
		for(n = i = 0; i < LIST_LENGTH(l); i++)
507
		  {
508
		    if (LIST(l)[i])
509
		      {
510
			UINT32 length = get_atom_length(LIST(l)[i]);
511
512
513
514
515
			
			if (n)
			  /* Not the first atom */
			  *buffer++ = ',';

516
			memcpy(buffer, get_atom_name(LIST(l)[i]), length);
517
			buffer += length;
518
519

			n++;
520
521
		      }
		  }
522

523
524
525
526
527
528
529
		if (!literal)
		  {
		    UINT32 total = buffer - start - 4;
		    WRITE_UINT32(start, total);
		  }
		break;
	      }
Niels Möller's avatar
Niels Möller committed
530
531
	    case 'n':
	      {
532
		MP_INT *n = va_arg(args, MP_INT *);
533
534
		UINT32 length;
		UINT8 *start = buffer; /* Where to store the length */
535
536
537

		/* Decimal not supported */
		assert(!decimal);
538
		
539
		if (!literal)
540
		  buffer += 4;
Niels Möller's avatar
Niels Möller committed
541

542
543
544
545
546
547
548
549
550
		if (unsigned_form)
		  {
		    assert(mpz_sgn(n) >= 0);
		
		    length = bignum_format_u(n, buffer);
		  }
		else
		  length = bignum_format_s(n, buffer);
		
Niels Möller's avatar
Niels Möller committed
551
		buffer += length;
Niels Möller's avatar
Niels Möller committed
552
553

		if (!literal)
554
		  WRITE_UINT32(start, length);
Niels Möller's avatar
Niels Möller committed
555

556
		break;
Niels Möller's avatar
Niels Möller committed
557
	      }
558
559
560
	    default:
	      fatal("ssh_vformat_write: bad format string");
	      break;
Niels Möller's avatar
Niels Möller committed
561
562
	    }
	}
Niels Möller's avatar
Niels Möller committed
563
564
565
566
      else
	{
	  *buffer++ = *f++;
	}
Niels Möller's avatar
Niels Möller committed
567
    }
568
569
  
  assert(buffer == start + size);
Niels Möller's avatar
Niels Möller committed
570
}
571

572
unsigned format_size_in_decimal(UINT32 n)
573
574
575
576
577
{
  int i;
  int e;
  
  /* Table of 10^(2^n) */
578
  static const UINT32 powers[] = { 10UL, 100UL, 10000UL, 100000000UL };
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596

#define SIZE (sizeof(powers) / sizeof(powers[0])) 

  /* Determine the smallest e such that n < 10^e */
  for (i = SIZE - 1 , e = 0; i >= 0; i--)
    {
      if (n >= powers[i])
	{
	  e += 1UL << i;
	  n /= powers[i];
	}
    }

#undef SIZE
  
  return e+1;
}

597
598
599
600
601
602
603
604
605
606
607
608
609

static void format_hex_string(UINT8 *buffer, UINT32 length, const UINT8 *data)
{
  static const UINT8 hexchars[16] = "0123456789abcdef";
  UINT32 i;

  for (i = 0; i < length; i++)
    {
      *buffer++ = hexchars[ (data[i] & 0xf0) >> 4 ];
      *buffer++ = hexchars[ data[i] & 0x0f ];
    }
}

610
void format_decimal(unsigned length, UINT8 *buffer, UINT32 n)
611
{
612
  unsigned i;
613
614
615
616
617
618
  
  for (i = 0; i<length; i++)
    {
      buffer[length - i - 1] = '0' + n % 10;
      n /= 10;
    }
619
620
621
622
623
}

static int write_decimal_length(UINT8 *buffer, UINT32 n)
{
  int length = format_size_in_decimal(n);
624

625
  format_decimal(length, buffer, n);
626
627
628
629
630
  buffer[length] = ':';

  return length + 1;
}

631
632
633
/* These functions add an extra NUL-character at the end of the string
 * (not included in the length), to make it possible to pass the
 * string directly to C library functions. */
634
635
struct lsh_string *
format_cstring(const char *s)
636
{
637
  return s ? ssh_cformat("%lz", s) : NULL;
638
639
}

640
struct lsh_string *
Niels Möller's avatar
Niels Möller committed
641
make_cstring_l(UINT32 length, const UINT8 *data)
642
{
643
  return ssh_cformat("%ls", length, data);
644
645
646
647
648
}

struct lsh_string *
make_cstring(struct lsh_string *s, int free)
{
Niels Möller's avatar
Niels Möller committed
649
  struct lsh_string *res = ssh_cformat("%lS", s);
650

651
652
  if (free)
    lsh_string_free(s);
653

654
655
  return res;
}
656

Niels Möller's avatar
Niels Möller committed
657
658
int
lsh_string_eq(const struct lsh_string *a, const struct lsh_string *b)
659
660
661
662
663
{
  return ( (a->length == b->length)
	   && !memcmp(a->data, b->data, a->length));
}

Niels Möller's avatar
Niels Möller committed
664
665
666
int
lsh_string_eq_l(const struct lsh_string *a,
		UINT32 length, const UINT8 *b)
667
668
669
670
671
{
  return ( (a->length == length)
	   && !memcmp(a->data, b, length));
}

672
673
674
int
lsh_string_prefixp(const struct lsh_string *prefix,
		   const struct lsh_string *s)
675
676
677
678
{
  return ( (prefix->length <= s->length)
	   && !memcmp(prefix->data, s->data, prefix->length));
}